Binary search tree

The binary search tree is a binary tree which holds ordered data. The ordering rule is as follows:

the value in each node is greater than the value in its left child and less than the value in its right child.

Applications

The data in a BST is basically always kept sorted and this allows to quickly lookup existing elements. Insert and remove are also fast, having complexities proportional to the tree height.

  • Data structures where data is constantly entering/leaving and a quick lookup is needed - map, set
  • Used as a base structure for many database engines
  • Used in computer graphics for determining object intersection

Representation

The operations in a BST often require redirecting pointers to parents or children, so the most effective representation is by using pointers

class Node
{
    Key value;
    Node leftChild;
    Node rightChild;
}

Operations

The Binary Search Tree supports the following operations:

  • Insert element - O(log n)
  • Remove element - O(log n)
  • Lookup element - O(log n)
  • Traverse in sorted order - O(n)

Insert

Insert starts from the root and compares the value of the element being inserted against the value of the existing node in order to decide in which subtree it should go. When there is no child at the link we should follow, insert the new element there

Insert

Insert(inserted value)

     1. Assign current = root

     2. Repeat

                If: current.value > inserted value

                          If: has_left_child(current)

                                  current = left_child(current), then goto 2

                          else:

                                  create_left_child(current, inserted value)

                If: current.value <= inserted value

                          If: has_right_child(current)

                                  current = right_child(current), then goto 2

                          else:

                                  create_right_child(current, inserted value)

Lookup

Lookup goes the same way as the insert - start from the root, compare values and follow one of the links.

 

Lookup(value)

     Assign current = root

     While current != null

                If: current.value == value

                                  return current

                If: current.value > value

                                  current = left_child(current)

                If: current.value < value

                                  current = right_child(current)

                         

 

Remove

Remove first finds the element, then finds the leftmost node in its right sub-tree and swaps their values. Then executes Remove recursively for that leftmost node. If a node to be removed has only one child, we can just redirect the links. If a node to be removed does not have any children, just remove it.

Remove

Remove(node) // should lookup the node before remove

    current = node

    If has_no_children(current)

           remove_from_parent(current)

    else if has_one_child(current)

           replace_in_parent(current, only_child(current))

    else

           replacement = left_most_child(current.right)

           current.value = replacement.value

           Remove(replacement)

Traverse

Traversing a tree is basically going through each of its nodes in a specific order

Pre-order

go through the current, then through the left subtree, then through the right

 

Preorder-Traverse(node)

            Print(node)

            Preorder-Traverse(node.left)

            Preorder-Traverse(node.right)

In-order

go through the left subtree, then through the current, then through the right

 

Preorder-Traverse(node)

            Preorder-Traverse(node.left)

            Print(node)

            Preorder-Traverse(node.right)

*Note: this will gives us the sorted sequence

Post-order

go through the left subtree, then through the right, then through the current

 

Preorder-Traverse(node)

            Preorder-Traverse(node.left)

            Preorder-Traverse(node.right)

            Print(node)

Balancing

The shape of the binary tree depends on the input data. In the worst case (inserting sorted values) it will look like a list

In this case operations will have complexity of O(n)

To resolve this issue, we need to balance the tree (on the figure - make 25 the root)

Balancing

Balancing turns out to be quite a complex task. There are different modifications of the BST which keep the data balanced. Some of them are:

  • Red-black tree - marks some of the links as red and others as black and uses this to represent a 2,3-tree like a normal BST.
  • AVL-tree - compares the heights of subtrees and applies rotations where needed. The first balanced BST invented.
  • Treap - asigns random integers to nodes and tries to keep the heap rule for them (tree + heap = treap). Applies rotations to keep the heap rule.

Balancing - rotations

Rotation is a operation which keeps the BST rule but changes the height of subtrees. Rotations are used by Treap and AVL trees to keep the tree balanced. There are two types of rotation: left rotation and right rotation - bringing the left child or the right child up

Binary Search Tree

By Ivan Todorov

Binary Search Tree

  • 1,173