AlgoVerse

Binary search tree: insert and delete

A new key walks down from the root by comparisons to an empty spot; deleting has three cases: a leaf, one child, and two children (using the successor).

In a binary search tree, for every node, the keys in its left subtree are smaller and the keys in its right subtree are larger (or equal). So every operation follows a single path down from the root: at each node one comparison says whether to go left or right.

Read the full explanation ↓
Input:
n=11 · insert 14, delete 6PDF export

Insert 14 into the tree, then delete 6, which has two children and is replaced by its successor (7).

  1. class Node:
  2. def __init__(self, key):
  3. self.key = key
  4. self.left = None
  5. self.right = None
  6. def bst(root, ops):
  7. for op, key in ops:
  8. if op == "insert":
  9. root = insert(root, key)
  10. else:
  11. root = delete(root, key)
  12. return root
  13. def insert(node, key):
  14. if node is None:
  15. return Node(key)
  16. if key < node.key:
  17. node.left = insert(node.left, key)
  18. else:
  19. node.right = insert(node.right, key)
  20. return node
  21. def delete(node, key):
  22. if node is None:
  23. return None
  24. if key < node.key:
  25. node.left = delete(node.left, key)
  26. elif key > node.key:
  27. node.right = delete(node.right, key)
  28. else:
  29. if node.left is None:
  30. return node.right
  31. if node.right is None:
  32. return node.left
  33. succ = node.right
  34. while succ.left is not None:
  35. succ = succ.left
  36. node.key = succ.key
  37. node.right = delete(node.right, succ.key)
  38. return node

Invariant: For every node, all keys in its left subtree are smaller and all keys in its right subtree are larger or equal.

Start

2346791315171820

Recursion tree

Filled orange = running call · Orange outline = still on the stack · Grey = finished

Local variables

Orange = changed in this step

root object

  • key = 15
  • left = Node(key=6, left=Node(key=3, left=Node(key=2, left=None, right=None), right=Node(key=4, left=None, right=None)), right=Node(key=7, left=None, right=Node(key=13, left=…, right=None)))
  • right = Node(key=18, left=Node(key=17, left=None, right=None), right=Node(key=20, left=None, right=None))

ops list

('insert', 14)('delete', 6)

Call stack

  1. bst()line 8
Step 1 of 58
Black = inserted, deleted · Orange = key stepcompare: 0/9 · create: 0/1 · inserted: 0/1 · successor: 0/1 · copy: 0/1 · unlink: 0/1 · deleted: 0/1

    How the work grows with input size

    Average of 8 random graphs per n (fixed seeds). Hollow marker = the input on screen.

    • comparisons
    • current input (n=11)
    0510153579111315n = number of keyscomparisons
    Show as a table
    ncomparisons
    35.3
    58.3
    78
    910.5
    1111.6
    1311.1
    1513.6
    Explanation

    The idea

    In a binary search tree, for every node, the keys in its left subtree are smaller and the keys in its right subtree are larger (or equal). So every operation follows a single path down from the root: at each node one comparison says whether to go left or right.

    Insert

    Start at the root and go down by comparisons until you reach an empty spot (None). The new node is created right there. A repeated key goes to the right.

    Delete: three cases

    1. A leaf: a node with no children is simply removed.
    2. One child: the child takes the node's place.
    3. Two children: find the node's successor (the smallest key in its right subtree, that is, its leftmost node). Copy the successor's key into the node, then delete the successor itself (it has at most one child) from the right subtree.

    In this recursive version every call returns the new root of its subtree, and the parent stores it in node.left or node.right, so the links fix themselves.

    Cost

    Each operation is O(h), where h is the height of the tree: log n for a balanced tree, n for a chain (keys inserted in sorted order). Self-balancing trees such as AVL and red-black trees keep the height at log n.

    What to watch in the trace

    • The orange node is node in the running call, and a dashed orange ring marks a node created or changed in this step.
    • In "delete the root with two children", watch succ walk down to the leftmost node of the right subtree.
    • The "every loop round" detail level shows only the end of each insert or delete.