In computer science, binary search trees (BST), sometimes called ordered or sorted binary trees, are a particular type of container: data structures that store “items” (such as numbers, names etc.) in memory. They allow fast lookup, addition and removal of items, and can be used to implement either dynamic sets of items, or lookup tables that allow finding an item by its key (e.g., finding the phone number of a person by name).

Binary search trees keep their keys in sorted order, so that lookup and other operations can use the principle of binary search: when looking for a key in a tree (or a place to insert a new key), they traverse the tree from root to leaf, making comparisons to keys stored in the nodes of the tree and deciding, on the basis of the comparison, to continue searching in the left or right subtrees. On average, this means that each comparison allows the operations to skip about half of the tree, so that each lookup, insertion or deletion takes time proportional to the logarithm of the number of items stored in the tree. This is much better than the linear time required to find items by key in an (unsorted) array, but slower than the corresponding operations on hash tables.

A binary search tree of size $9$ and depth $3$, with $8$ at the root. The leaves are not drawn.

Binary Search Tree

Pseudocode

Basic Operations

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    
    insert(value)
      Pre: value has passed custom type checks for type T
      Post: value has been placed in the correct location in the tree
      if root = ø
        root ← node(value)
      else
        insertNode(root, value)
      end if
    end insert
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    contains(root, value)
      Pre: root is the root node of the tree, value is what we would like to locate
      Post: value is either located or not
      if root = ø
        return false
      end if
      if root.value = value
        return true
      else if value < root.value
        return contains(root.left, value)
      else
        return contains(root.right, value)
      end if
    end contains
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    remove(value)
      Pre: value is the value of the node to remove, root is the node of the BST
          count is the number of items in the BST
      Post: node with value is removed if found in which case yields true, otherwise false
      nodeToRemove ← findNode(value)
      if nodeToRemove = ø
        return false
      end if
      parent ← findParent(value)
      if count = 1
        root ← ø
      else if nodeToRemove.left = ø and nodeToRemove.right = ø
        if nodeToRemove.value < parent.value
          parent.left ←  nodeToRemove.right
        else
          parent.right ← nodeToRemove.right
        end if
      else if nodeToRemove.left = ø and nodeToRemove.right = ø
       if nodeToRemove.value < parent.value
         parent.left ←  nodeToRemove.left
       else
         parent.right ← nodeToRemove.left
       end if
      else
       largestValue ← nodeToRemove.left
       while largestValue.right = ø
         largestValue ← largestValue.right
       end while
       findParent(largestValue.value).right ← ø
       nodeToRemove.value ← largestValue.value
      end if
      count ← count - 1
      return true
    end remove
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    
    findMin(root)
      Pre: root is the root node of the BST
        root = ø
      Post: the smallest value in the BST is located
      if root.left = ø
        return root.value
      end if
      findMin(root.left)
    end findMin
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    
    findMax(root)
      Pre: root is the root node of the BST
        root = ø
      Post: the largest value in the BST is located
      if root.right = ø
        return root.value
      end if
      findMax(root.right)
    end findMax
    

Node Operations

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    insertNode(current, value)
      Pre: current is the node to start from
      Post: value has been placed in the correct location in the tree
      if value < current.value
        if current.left = ø
          current.left ← node(value)
        else
          InsertNode(current.left, value)
        end if
      else
        if current.right = ø
          current.right ← node(value)
        else
          InsertNode(current.right, value)
        end if
      end if
    end insertNode
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    findNode(root, value)
      Pre: value is the value of the node we want to find the parent of
           root is the root node of the BST
      Post: a reference to the node of value if found; otherwise ø
      if root = ø
        return ø
      end if
      if root.value = value
        return root
      else if value < root.value
        return findNode(root.left, value)
      else
        return findNode(root.right, value)
      end if
    end findNode
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    findParent(value, root)
      Pre: value is the value of the node we want to find the parent of
           root is the root node of the BST and is != ø
      Post: a reference to the prent node of value if found; otherwise ø
      if value = root.value
        return ø
      end if
      if value < root.value
        if root.left = ø
          return ø
        else if root.left.value = value
          return root
        else
          return findParent(value, root.left)
        end if
      else
        if root.right = ø
          return ø
        else if root.right.value = value
          return root
        else
          return findParent(value, root.right)
        end if
      end if
    end findParent
    

Traversal

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    
    inorder(root)
      Pre: root is the root node of the BST
      Post: the nodes in the BST have been visited in inorder
      if root = ø
        inorder(root.left)
        yield root.value
        inorder(root.right)
      end if
    end inorder
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    
    preorder(root)
      Pre: root is the root node of the BST
      Post: the nodes in the BST have been visited in preorder
      if root = ø
        yield root.value
        preorder(root.left)
        preorder(root.right)
      end if
    end preorder
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    
    postorder(root)
      Pre: root is the root node of the BST
      Post: the nodes in the BST have been visited in postorder
      if root = ø
        postorder(root.left)
        postorder(root.right)
        yield root.value
      end if
    end postorder
    

Complexities

Time Complexity

Access Search Insertion Deletion
$O(\log(n))$ $O(\log(n))$ $O(\log(n))$ $O(\log(n))$

Space Complexity

$O(n)$

Implementations

Insertion

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    const T = {};
    
    const elements = [5, 8, 10, 3, 1, 6, 9, 7, 2, 0, 4]; // item to be inserted
    
    function bstInsert(root, element, parent) { // root = current node , parent = previous node
      
      const treeNode = T[root];
      let propName = '';
      if (element < root) {
        propName = 'left';
      } else if (element > root) {
        propName = 'right';
      }
      if (propName !== '') {
        if (!(propName in treeNode)) { // insert as left child of root
          treeNode[propName] = element;
          T[element] = {};
          
        } else {
          bstInsert(treeNode[propName], element, root);
        }
      }
      
    }
    
    const Root = elements[0]; // take first element as root
    T[Root] = {};
    
    for (let i = 1; i < elements.length; i++) {
      
      bstInsert(Root, elements[i]); // insert ith element
      
    }
    
  • 1
    
    # TODO: Implement in Python
    
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    const T = {};
    
    const elements = [5, 8, 10, 3, 1, 6, 9, 7, 2, 0, 4]; // item to be inserted
    
    function bstInsert(root, element, parent) { // root = current node , parent = previous node
      
      const treeNode = T[root];
      let propName = '';
      if (element < root) {
        propName = 'left';
      } else if (element > root) {
        propName = 'right';
      }
      if (propName !== '') {
        if (!(propName in treeNode)) { // insert as left child of root
          treeNode[propName] = element;
          T[element] = {};
          
        } else {
          bstInsert(treeNode[propName], element, root);
        }
      }
      
    }
    
    const Root = elements[0]; // take first element as root
    T[Root] = {};
    
    for (let i = 1; i < elements.length; i++) {
      
      bstInsert(Root, elements[i]); // insert ith element
      
    }
    

References