Balancing Search Trees

COMP2521 20T1 ♢ Balancing Search Trees ♢ [0/30]
❖ Balancing Binary Search Trees


Observation: order of insertion into a tree affects its height

Goal: build binary search trees which have
COMP2521 20T1 ♢ Balancing Search Trees ♢ [1/30]
❖ ... Balancing Binary Search Trees


Perfectly-balanced tree with N nodes has

Three strategies to improving worst case search in BSTs:
COMP2521 20T1 ♢ Balancing Search Trees ♢ [2/30]
❖ Operations for Rebalancing

To assist with rebalancing, we consider new operations:

Left rotation

Right rotation Insertion at root Partition
COMP2521 20T1 ♢ Balancing Search Trees ♢ [3/30]
❖ Tree Rotation


Rotation operations:

[Diagram:Pics/left-right-rotation.png]

Note: tree is ordered,   t1  <  n2  <  t2  <  n1  <  t3

COMP2521 20T1 ♢ Balancing Search Trees ♢ [4/30]
❖ ... Tree Rotation

Method for rotating tree T right:

Left rotation: swap left/right in the above.

Rotation requires simple, localised pointer rearrangemennts

Cost of tree rotation: O(1)

COMP2521 20T1 ♢ Balancing Search Trees ♢ [5/30]
❖ ... Tree Rotation


Example of right rotation:


[Diagram:Pics/rotr.png]

COMP2521 20T1 ♢ Balancing Search Trees ♢ [6/30]
❖ ... Tree Rotation

Algorithm for right rotation:

rotateRight(n1):
|  Input  tree n1
|  Output n1 rotated to the right
|
|  if n1 is empty ∨ left(n1) is empty then
|     return n1
|  end if
|  n2=left(n1)
|  left(n1)=right(n2)
|  right(n2)=n1
|  return n2

COMP2521 20T1 ♢ Balancing Search Trees ♢ [7/30]
❖ ... Tree Rotation

Algorithm for left rotation:

rotateLeft(n2):
|  Input  tree n2
|  Output n2 rotated to the left
|
|  if n2 is empty ∨ right(n2) is empty then
|     return n2
|  end if
|  n1=right(n2)
|  right(n2)=left(n1)
|  left(n1)=n2
|  return n1

COMP2521 20T1 ♢ Balancing Search Trees ♢ [8/30]
❖ ... Tree Rotation


Cost considerations for tree rotation

Sometimes rotation is applied from leaf to root, along one branch
COMP2521 20T1 ♢ Balancing Search Trees ♢ [9/30]
❖ Insertion at Root


Previous discussion of BSTs did insertion at leaves.

Different approach: insert new item at root.

Potential disadvantages:

Potential advantages:
COMP2521 20T1 ♢ Balancing Search Trees ♢ [10/30]
❖ ... Insertion at Root


Method for inserting at root:

COMP2521 20T1 ♢ Balancing Search Trees ♢ [11/30]
❖ ... Insertion at Root


Example of inserting at root:


[Diagram:Pics/insert-root.png]

COMP2521 20T1 ♢ Balancing Search Trees ♢ [12/30]
❖ ... Insertion at Root

Algorithm for inserting at root:

insertAtRoot(t, it):
|  Input tree t, item it to be inserted
|  Output modified tree with item at root
|
|  if t is empty tree then
|     t = new node containing item
|  else if item < root(t) then
|     left(t) = insertAtRoot(left(t), it)
|     t = rotateRight(t)
|  else if it > root(t) then
|     right(t) = insertAtRoot(right(t), it)
|     t = rotateLeft(t)
|  end if
|  return t;

COMP2521 20T1 ♢ Balancing Search Trees ♢ [13/30]
❖ ... Insertion at Root


Analysis of insertion-at-root:

COMP2521 20T1 ♢ Balancing Search Trees ♢ [14/30]
❖ Tree Partitioning

Tree partition operation partition(tree,i)


[Diagram:Pics/tindex.png]


For tree with N nodes, indices are 0 .. N-1, in LNR order

COMP2521 20T1 ♢ Balancing Search Trees ♢ [15/30]
❖ ... Tree Partitioning


Example of partition:


[Diagram:Pics/partition.png]

COMP2521 20T1 ♢ Balancing Search Trees ♢ [16/30]
❖ ... Tree Partitioning

Implementation of partition operation:

partition(tree,i):
|  Input  tree with n nodes, index i
|  Output tree with ith item moved to the root
|
|  m=#nodes(left(tree))
|  if i < m then
|     left(tree)=partition(left(tree),i)
|     tree=rotateRight(tree)
|  else if i > m then
|     right(tree)=partition(right(tree),i-m-1)
|     tree=rotateLeft(tree)
|  end if
|  return tree

Note:  size(tree) = n,   size(left(tree)) = m,   size(right(tree)) = n-m-1

COMP2521 20T1 ♢ Balancing Search Trees ♢ [17/30]
❖ ... Tree Partitioning


Analysis of tree partitioning

Benefits
COMP2521 20T1 ♢ Balancing Search Trees ♢ [18/30]
❖ Periodic Rebalancing

An approach to maintaining balance:

|  Input  tree, item
|  Output tree with item randomly inserted
|
|  t=insertAtLeaf(tree,item)
|  if #nodes(t) mod k = 0 then
|     t=rebalance(t)
|  end if
|  return t

When to rebalance?   e.g. after every k insertions

COMP2521 20T1 ♢ Balancing Search Trees ♢ [19/30]
❖ ... Periodic Rebalancing


A problem with this approach ...

typedef struct Node {
   int  data;
   int  nnodes;      // #nodes in my tree
   Tree left, right; // subtrees
} Node;

But maintaining nnodes requires extra work in other operations

COMP2521 20T1 ♢ Balancing Search Trees ♢ [20/30]
❖ ... Periodic Rebalancing


How to rebalance a BST?   Move median item to root.

[Diagram:Pics/rebalance.png]

COMP2521 20T1 ♢ Balancing Search Trees ♢ [21/30]
❖ ... Periodic Rebalancing

Implementation of rebalance:

rebalance(t):
|  Input  tree t with n nodes
|  Output t rebalanced
|
|  if n≥3 then
|  |  // put node with median key at root
|  |  t=partition(t,n/2)
|  |  // then rebalance each subtree
|  |  left(t)=rebalance(left(t))
|  |  right(t)=rebalance(right(t))
|  end if
|  return t

COMP2521 20T1 ♢ Balancing Search Trees ♢ [22/30]
❖ ... Periodic Rebalancing

Analysis of rebalancing: visits every node ⇒ O(N)

Cost means not feasible to rebalance after each insertion.

When to rebalance? … Some possibilities:

Either way, we tolerate worse search performance for periods of time.

Does it solve the problem? … Not completely   ⇒ Solution: real balanced trees  (next week)

COMP2521 20T1 ♢ Balancing Search Trees ♢ [23/30]
❖ Randomised BST Insertion


Reminder: order of insertion can dramatically affect shape of tree

Tree ADT has no control over order that keys are supplied.

We know that inserting in random order gives O(log2n) search

Can the algorithm itself introduce some randomness?

In the hope that this randomness helps to balance the tree …

COMP2521 20T1 ♢ Balancing Search Trees ♢ [24/30]
❖ ... Randomised BST Insertion

Approach: normally do leaf insert, randomly do root insert.

insertRandom(tree,item)
|  Input  tree, item
|  Output tree with item randomly inserted
|
|  if tree is empty then
|     return new node containing item
|  end if
|  // p/q chance of doing root insert
|  if random() mod q < p then
|     return insertAtRoot(tree,item)
|  else
|     return insertAtLeaf(tree,item)
|  end if

E.g. 30% chance ⇒ choose p=3, q=10

COMP2521 20T1 ♢ Balancing Search Trees ♢ [25/30]
❖ ... Randomised BST Insertion


Cost analysis:

Approach can also be applied to deletion:
COMP2521 20T1 ♢ Balancing Search Trees ♢ [26/30]
❖ An Application of BSTs: Sets


Trees provide efficient search.

Sets require efficient search

Logical to implement a set ADT via binary search tree.
COMP2521 20T1 ♢ Balancing Search Trees ♢ [27/30]
❖ ... An Application of BSTs: Sets


Assuming we have BST implementation with type Tree

then Set implementation is What about union? and intersection?
COMP2521 20T1 ♢ Balancing Search Trees ♢ [28/30]
❖ ... An Application of BSTs: Sets


Sets implemented via Trees:

[Diagram:Pics/set-tree.png]

COMP2521 20T1 ♢ Balancing Search Trees ♢ [29/30]
❖ ... An Application of BSTs: Sets

Concrete representation:

#include <Tree.h>

typedef struct SetRep {
   int   nelems;
   Tree  root;
} SetRep;

Set newSet() {
   Set S = malloc(sizeof(SetRep));
   assert(S != NULL);
   S->nelems = 0;
   S->root = newTree();
   return S;
}

COMP2521 20T1 ♢ Balancing Search Trees ♢ [30/30]


Produced: 8 Jun 2020