Binary search trees
A binary tree kept in order so that search, insert and delete follow one path from the root. Learn the ordering rule, the three cases of deletion, why the shape (and so the running time) depends on the insertion order, the successor, the k-th smallest, how to rebuild a tree from its preorder, and the common wrong test for whether a tree is a BST.
Why it exists
To find a value in an unsorted list you may have to look at all of it. A sorted array can be searched quickly but is slow to insert into. A binary search tree (BST) keeps the data in a branching structure that is ordered, so that a search only follows one path from the root, and an insertion or deletion changes only a few pointers. When the tree is reasonably bushy, all three operations take time proportional to .
Prerequisites
Precise definitions
- Binary search tree. A binary tree with a key in every node such that for every node : all keys in the left subtree of are smaller than the key of , and all keys in the right subtree are larger. (The rule is about whole subtrees, not just the two children. With duplicate keys a convention must be chosen, such as "equal keys go right", and the question will state it. This page uses distinct keys.)
- Inorder is sorted. The inorder traversal of a BST lists the keys in increasing order. Conversely (for distinct keys) a binary tree whose inorder is strictly increasing is a BST.
- Search for : compare with the root; if equal stop; if smaller go left; if larger go right; stop at
NULL(not found). One path, so the time is where is the height. - Minimum is the leftmost node; maximum the rightmost.
- Successor of a key: the smallest key larger than it. If the node has a right subtree, it is the minimum of that subtree; otherwise it is the lowest ancestor for which the node lies in the left subtree. The predecessor is the mirror image.
- Insert : search for ; the search ends at a
NULLlink; put a new node there. The new node is always a leaf. - Delete : find the node holding , then:
- No children (leaf): remove it.
- One child: replace by its child (the child's whole subtree moves up).
- Two children: find the inorder successor (the minimum of the right subtree), copy 's key into , then delete from the right subtree. (Copying the key is fine for plain
intkeys. CLRS's TREE-DELETE instead relinks the nodes themselves, which matters if other pointers refer to nodes or the nodes carry extra data.) has no left child, so that deletion is case 1 or 2. (Using the inorder predecessor, the maximum of the left subtree, is equally correct and gives a different tree. A question will state which one it wants.)
- Cost. Search, insert and delete all take . For keys the height is at least and at most .
- The shape depends on the insertion order. The same keys can give a perfectly balanced tree or a chain. Keys inserted in sorted order always make a chain, and the operations then take . For keys inserted in random order the expected height is . Trees that repair their own shape (AVL, red-black) keep the height in the worst case.
- Inserting in preorder order rebuilds the same tree. If you insert the keys of a BST's preorder list one by one into an empty tree, you get back the same tree. Every node is inserted after its ancestors, and no node inserted earlier except its ancestors lies on its search path (the earlier ones are in earlier sibling subtrees), so each node lands exactly where it was. (Postorder fails this test, though its reverse works.)
How it works
Insertion
Insertion is a search that ends by creating a node. The blue list is the path from the root, which is the recursion stack of insert(). Watch the last key, 35: it follows 50, then 30, then 40, and is attached as the left child of 40. Every new node is a leaf.
Inserting keys into a binary search tree
Real runC source
Tree (read from the program's real pointers)
Search path (the nodes the recursion has passed through)
Blue ring = on the call stack (or the search path), amber ring = waiting in the queue, green with a number = already printed (and in which order). The filled node is the one this step is about.
Step 1
reached NULL: a new node holding 50 is created here and returned to be linked in
The shape depends on the order
The same function now gets the keys in increasing order. Each key is larger than everything so far, so it follows the right links all the way down and ends as the right child of the previous key. The result is a chain of height : a BST that is as slow as a linked list.
Inserting sorted keys makes a chain
Real runC source
Tree (read from the program's real pointers)
Search path (the nodes the recursion has passed through)
Blue ring = on the call stack (or the search path), amber ring = waiting in the queue, green with a number = already printed (and in which order). The filled node is the one this step is about.
Step 1
reached NULL: a new node holding 10 is created here and returned to be linked in
Deletion: leaf, one child, two children
A tree is built from 50, 30, 70, 20, 40, 60, 80, 65, and three keys are deleted. Deleting 20 is the leaf case. Deleting 30 (which now has only a right child 40) is the one-child case: 40 takes its place. Deleting 50, which has two children, copies the successor key 60 (the minimum of its right subtree) into the root and then deletes the old node 60, which has only a right child 65.
Deleting from a binary search tree: leaf, one child, two children
Real runC source
Tree (read from the program's real pointers)
Search path (the nodes the recursion has passed through)
Blue ring = on the call stack (or the search path), amber ring = waiting in the queue, green with a number = already printed (and in which order). The filled node is the one this step is about.
Step 1
The tree built from 50, 30, 70, 20, 40, 60, 80, 65. Now delete 20 (a leaf), then 30 (one child left), then 50 (two children).
Worked examples
EX 1Build a BST and read it three ways
Easy#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *left, *right;
};
static struct node *insert(struct node *t, int x) {
if (t == NULL) {
t = calloc(1, sizeof *t);
if (t == NULL) exit(1);
t->data = x;
return t;
}
if (x < t->data)
t->left = insert(t->left, x);
else if (x > t->data)
t->right = insert(t->right, x);
return t;
}
static void in(const struct node *t) {
if (t == NULL) return;
in(t->left);
printf("%d ", t->data);
in(t->right);
}
static void pre(const struct node *t) {
if (t == NULL) return;
printf("%d ", t->data);
pre(t->left);
pre(t->right);
}
static int height(const struct node *t) {
if (t == NULL) return -1;
int l = height(t->left), r = height(t->right);
return 1 + (l > r ? l : r);
}
int main(void) {
int keys[7] = {50, 30, 70, 20, 40, 60, 35};
struct node *root = NULL;
for (int i = 0; i < 7; i++) root = insert(root, keys[i]);
in(root);
printf("\n");
pre(root);
printf("\n%d\n", height(root));
return 0;
}Output:
20 30 35 40 50 60 70
50 30 20 40 35 70 60
3Reading it. The inorder list is sorted, as it must be. The preorder list tells the shape: 50 is the root; 30, 20, 40, 35 form the left part (30 is its root, 20 its left child, 40 its right child, 35 below 40); 70 and 60 form the right part. The longest path is 50, 30, 40, 35: height 3.
EX 2Search, and how many nodes it looks at
Easy#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *left, *right;
};
static struct node *insert(struct node *t, int x) {
if (t == NULL) {
t = calloc(1, sizeof *t);
if (t == NULL) exit(1);
t->data = x;
return t;
}
if (x < t->data)
t->left = insert(t->left, x);
else if (x > t->data)
t->right = insert(t->right, x);
return t;
}
static int search(const struct node *t, int key, int *examined) {
*examined = 0;
while (t != NULL) {
(*examined)++;
if (key == t->data) return 1;
t = key < t->data ? t->left : t->right;
}
return 0;
}
int main(void) {
int keys[7] = {50, 30, 70, 20, 40, 60, 35};
struct node *root = NULL;
for (int i = 0; i < 7; i++) root = insert(root, keys[i]);
int c1, c2;
int f1 = search(root, 35, &c1);
int f2 = search(root, 45, &c2);
printf("%d %d %d %d\n", f1, c1, f2, c2);
return 0;
}Output:
1 4 0 3Reading it. Searching for 35 examines 50, 30, 40 and 35: 4 nodes (found). Searching for 45 examines 50 (go left), 30 (go right), 40 (go right), and the right link of 40 is NULL: 3 nodes examined, then not found. The number of nodes examined is at most the height plus one for a successful search. For an unsuccessful search it is the depth of the last node reached plus one.
EX 3Minimum, maximum, successor and predecessor
Medium#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *left, *right;
};
static struct node *insert(struct node *t, int x) {
if (t == NULL) {
t = calloc(1, sizeof *t);
if (t == NULL) exit(1);
t->data = x;
return t;
}
if (x < t->data)
t->left = insert(t->left, x);
else if (x > t->data)
t->right = insert(t->right, x);
return t;
}
static int minimum(const struct node *t) {
while (t->left != NULL) t = t->left;
return t->data;
}
static int maximum(const struct node *t) {
while (t->right != NULL) t = t->right;
return t->data;
}
static int successor(const struct node *t, int key) {
int s = -1;
while (t != NULL) {
if (key < t->data) {
s = t->data;
t = t->left;
} else
t = t->right;
}
return s;
}
static int predecessor(const struct node *t, int key) {
int p = -1;
while (t != NULL) {
if (key > t->data) {
p = t->data;
t = t->right;
} else
t = t->left;
}
return p;
}
int main(void) {
int keys[7] = {50, 30, 70, 20, 40, 60, 35};
struct node *root = NULL;
for (int i = 0; i < 7; i++) root = insert(root, keys[i]);
printf("%d %d\n", minimum(root), maximum(root));
printf("%d %d %d %d\n", successor(root, 35), successor(root, 40), successor(root, 70), predecessor(root, 50));
return 0;
}Output:
20 70
40 50 -1 40Reading it. The minimum is the leftmost node (20) and the maximum the rightmost (70). To find the successor of a key, walk down from the root as in a search and remember the last node where you went left: that is the smallest key seen that is larger than the target. The successor of 35 is 40 (its parent), of 40 is 50 (the root: 40 has no right subtree, so the answer is the lowest ancestor of which it is in the left subtree), and 70 has no successor (printed as ). The predecessor of 50 is the maximum of its left subtree, 40.
EX 4Different insertion orders, different trees
GATE level#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *left, *right;
};
static struct node *insert(struct node *t, int x) {
if (t == NULL) {
t = calloc(1, sizeof *t);
if (t == NULL) exit(1);
t->data = x;
return t;
}
if (x < t->data)
t->left = insert(t->left, x);
else if (x > t->data)
t->right = insert(t->right, x);
return t;
}
static int height(const struct node *t) {
if (t == NULL) return -1;
int l = height(t->left), r = height(t->right);
return 1 + (l > r ? l : r);
}
static int code, digits;
static void pre(const struct node *t) {
if (t == NULL) return;
code = code * 10 + t->data;
digits++;
pre(t->left);
pre(t->right);
}
int main(void) {
int orders[6][3] = {{1, 2, 3}, {1, 3, 2}, {2, 1, 3}, {2, 3, 1}, {3, 1, 2}, {3, 2, 1}};
int seen[6], nseen = 0;
for (int o = 0; o < 6; o++) {
struct node *root = NULL;
for (int i = 0; i < 3; i++) root = insert(root, orders[o][i]);
printf("%d ", height(root));
code = 0;
digits = 0;
pre(root);
int dup = 0;
for (int j = 0; j < nseen; j++) dup = dup || seen[j] == code;
if (!dup) seen[nseen++] = code;
}
printf("\n%d\n", nseen);
return 0;
}Output:
2 2 1 1 2 2
5Reading it. The six orders of the keys 1, 2, 3 give heights 2, 2, 1, 1, 2, 2. Only the orders that start with the middle key 2 (2, 1, 3 and 2, 3, 1) give the balanced tree of height 1, and both give the same tree. So the six orders give only 5 different trees, the Catalan number . The six orders are not equally likely to produce each tree: the balanced tree comes from 2 of the 6 orders (the formula ), each of the four chains from exactly 1.
EX 5From a preorder list to the postorder
GATE level#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *left, *right;
};
static struct node *insert(struct node *t, int x) {
if (t == NULL) {
t = calloc(1, sizeof *t);
if (t == NULL) exit(1);
t->data = x;
return t;
}
if (x < t->data)
t->left = insert(t->left, x);
else if (x > t->data)
t->right = insert(t->right, x);
return t;
}
static void post(const struct node *t) {
if (t == NULL) return;
post(t->left);
post(t->right);
printf("%d ", t->data);
}
int main(void) {
int pre[7] = {50, 30, 20, 40, 70, 60, 80};
struct node *root = NULL;
for (int i = 0; i < 7; i++) root = insert(root, pre[i]);
post(root);
printf("\n");
return 0;
}Output:
20 40 30 60 80 70 50Reading it. For a BST, the preorder list alone is enough: inserting its keys in that order rebuilds the same tree, and then any other traversal can be read off. By hand, without building the tree: the first key (50) is the root; the keys after it that are smaller (30, 20, 40) form the left subtree and the larger ones (70, 60, 80) the right; repeat inside each part. Postorder lists left subtree, right subtree, root: 20 40 30, 60 80 70, 50.
EX 6The wrong test for a binary search tree
GATE level#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *left, *right;
};
static struct node *mk(int d, struct node *l, struct node *r) {
struct node *t = malloc(sizeof *t);
if (t == NULL) exit(1);
t->data = d;
t->left = l;
t->right = r;
return t;
}
static int local_ok(const struct node *t) {
if (t == NULL) return 1;
if (t->left && t->left->data >= t->data) return 0;
if (t->right && t->right->data <= t->data) return 0;
return local_ok(t->left) && local_ok(t->right);
}
static int bounds_ok(const struct node *t, long lo, long hi) {
if (t == NULL) return 1;
if (t->data <= lo || t->data >= hi) return 0;
return bounds_ok(t->left, lo, t->data) && bounds_ok(t->right, t->data, hi);
}
int main(void) {
struct node *r = mk(10, mk(5, mk(1, NULL, NULL), mk(12, NULL, NULL)), mk(15, NULL, NULL));
printf("%d %d\n", local_ok(r), bounds_ok(r, LONG_MIN, LONG_MAX));
return 0;
}Output:
1 0Reading it. In this tree every node is larger than its left child and smaller than its right child, so the local test says yes. But 12 is in the left subtree of 10 and is larger than 10, so the tree is not a BST: the rule applies to the whole subtree. The correct test passes down the allowed interval : going left, the upper bound becomes the node's key; going right, the lower bound becomes the node's key. The starting interval must lie outside every possible key, which is why the code starts with LONG_MIN and LONG_MAX (an arbitrary range such as would wrongly reject a valid tree that holds a larger key). The simplest equivalent test: the inorder traversal must be strictly increasing.
EX 7The k-th smallest key
Medium#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *left, *right;
};
static struct node *insert(struct node *t, int x) {
if (t == NULL) {
t = calloc(1, sizeof *t);
if (t == NULL) exit(1);
t->data = x;
return t;
}
if (x < t->data)
t->left = insert(t->left, x);
else if (x > t->data)
t->right = insert(t->right, x);
return t;
}
static int seen, answer;
static void kth(const struct node *t, int k) {
if (t == NULL || seen >= k) return;
kth(t->left, k);
if (++seen == k) answer = t->data;
kth(t->right, k);
}
int main(void) {
int keys[7] = {50, 30, 70, 20, 40, 60, 35};
struct node *root = NULL;
for (int i = 0; i < 7; i++) root = insert(root, keys[i]);
seen = 0;
kth(root, 3);
int a = answer;
seen = 0;
kth(root, 6);
printf("%d %d\n", a, answer);
return 0;
}Output:
35 60Reading it. Inorder visits the keys in increasing order, so the -th key visited is the -th smallest. The function counts as it goes and stops descending once the count reaches . The sorted list is 20 30 35 40 50 60 70, so the 3rd is 35 and the 6th is 60. The time is . The function assumes (for a larger it would silently leave the previous answer in place), and it keeps its state in global variables, so it is not reusable as it stands.
EX 8Deleting keys and reading the preorder after each
GATE level#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *left, *right;
};
static struct node *insert(struct node *t, int x) {
if (t == NULL) {
t = calloc(1, sizeof *t);
if (t == NULL) exit(1);
t->data = x;
return t;
}
if (x < t->data)
t->left = insert(t->left, x);
else if (x > t->data)
t->right = insert(t->right, x);
return t;
}
static struct node *minnode(struct node *t) {
while (t->left != NULL) t = t->left;
return t;
}
static struct node *delete(struct node *t, int x) {
if (t == NULL) return NULL;
if (x < t->data) {
t->left = delete(t->left, x);
} else if (x > t->data) {
t->right = delete(t->right, x);
} else if (t->left == NULL) {
struct node *r = t->right;
free(t);
return r;
} else if (t->right == NULL) {
struct node *l = t->left;
free(t);
return l;
} else {
struct node *m = minnode(t->right);
t->data = m->data;
t->right = delete(t->right, m->data);
}
return t;
}
static void pre(const struct node *t) {
if (t == NULL) return;
printf("%d ", t->data);
pre(t->left);
pre(t->right);
}
int main(void) {
int keys[8] = {50, 30, 70, 20, 40, 60, 80, 65};
int del[3] = {20, 30, 50};
struct node *root = NULL;
for (int i = 0; i < 8; i++) root = insert(root, keys[i]);
pre(root);
printf("\n");
for (int i = 0; i < 3; i++) {
root = delete(root, del[i]);
pre(root);
printf("\n");
}
return 0;
}Output:
50 30 20 40 70 60 65 80
50 30 40 70 60 65 80
50 40 70 60 65 80
60 40 70 65 80Reading it. Deleting 20 (a leaf) just removes it. Deleting 30, which now has only the right child 40, puts 40 in its place. Deleting 50, which has two children, copies the inorder successor 60 into the root and removes the old 60 node; that node had only a right child 65, which moves up to become the left child of 70. If the predecessor (the maximum of the left subtree, 40) had been used instead, the root would become 40 and the final preorder would be 40 70 60 65 80. Both trees are valid BSTs: which one is expected is stated by the question.
Cheat box
Cheat box
- BST: every key in the left subtree node every key in the right subtree. Inorder is sorted.
- Search, insert, delete: . Height between and .
- Insert: always a new leaf at the end of the search path.
- Delete: leaf remove; one child child takes its place; two children copy the inorder successor (min of right subtree) or predecessor (max of left), then delete that node.
- Min: leftmost. Max: rightmost. Successor: min of right subtree, else lowest ancestor reached by going left.
- Sorted insertion order chain. orders, trees.
- Inserting the preorder list rebuilds the tree. A preorder, a postorder (read in reverse) or a level order alone determines a BST; inorder alone never does (it is just the sorted keys).
- To test a BST: pass down bounds, or check that inorder is strictly increasing. Comparing only with the children is wrong.
- -th smallest: inorder with a counter.
Traps and GATE patterns
Connections
Builds on
Unlocks
Across subjects
The BST is a binary tree (trees) whose inorder traversal is sorted. How many different BSTs exist is the Catalan question on the counting page, and rebuilding one from a list is on the construction page. Self-balancing versions that keep the height logarithmic are AVL trees. A heap is also a binary tree with an ordering rule, but a weaker one (parent versus children only), which is why it cannot search quickly. Database indexes use the multiway version, B-trees.
PYQs
12 previous year questions from your book. I re-solved each one myself (7 by running or simulating, 5 by reasoning only); the book's key is shown next to my answer. All PYQs
Suppose we have a balanced binary search tree T holding n numbers. We are given two numbers L and H and wish to sum up all the numbers in T that lie between L and H. Suppose there are m such numbers in T. If the tightest upper bound on the time to compute the sum is O(na logb n + mc logd n) the value of a + 10b + 100c + 1000d is . for (j=2; j<=n; j=j*2) k = k + n/2; return (k); } The return value of the function is
- (a)Θ(n2)
- (b)Θ(n2log n)
- (c)Θ(n3)
- (d)Θ(n3log n)
A meld operation on two instances of a data structure combines them into one single instance of the same data structure. Consider the following data structures: P: Unsorted doubly linked list with pointers to the head node and tail node of the list. Q: Min-heap implemented using an array. R: Binary Search Tree. Which ONE of the following options gives the worst-case time complexities for meld operation on instances of size n of these data structures?
- (a)P: θ(1), Q: θ(n), R: θ(n)
- (b)P: θ(1), Q: θ(nlogn), R: θ(n)
- (c)P: θ(n), Q: θ(nlogn), R: θ(n2)
- (d)P: θ(1), Q: θ(n), R: θ(nlogn)
Consider a binary search tree (BST) with n leaf nodes (n > 0). Given any node V, the key present in the node is denoted as Val(V). All the keys present in the given BST are distinct. The keys belong to the set of real numbers. For a node V, let Suc (V) denote the node that is its inorder successor. If a node V does not have an inorder successor, then Suc (V) is NULL. As there are no duplicates, if Suc (V) is not NULL, then Val(V) < Val(Suc(V)). Corresponding to every leaf node L1 that has a non- NULL Suc (Li), a new key ki with the following property is to be inserted into the BST. Val(Li) <ki < Val(Suc(Li)) Let K represent the list of all such new keys to be inserted into the BST. Which of the following statements is/are true?
- (a)K cannot have any duplicates
- (b)K will have at least one element
- (c)After inserting all keys from K, the height of the BST can increase at most by one
- (d)Number of nodes in the BST will double after inserting all keys from K
A binary search T contains n distinct elements. What is the time complexity of picking an element in T that is smaller than the maximum element in T?
- (a)Θ(n)
- (b)Θ(log n)
- (c)Θ(n log n)
- (d)Θ(1)
In a balanced binary search tree with n elements, what is the worst case time complexity of reporting all elements in range [a,b]? Assume that the number of reported elements is k.
- (a)Θ(n log k)
- (b)Θ(log n + k)
- (c)Θ(k log n)
- (d)Θ(logn) DATA STRUCTURE
The number of ways in which the numbers 1, 2, 3, 4, 5, 6, 7 can be inserted in an empty binary search tree, such that the resulting tree has height 6, is DATA STRUCTURE Note: The height of a tree with a single node is 0. [This question was originally a Fill-in-the-Blanks question]
- (a)2
- (b)4
- (c)64
- (d)32
While inserting the elements 71, 65, 84, 69, 67, 83 in an empty binary search tree (BST) in the sequence shown, the element in the lowest level is
- (a)65
- (b)67
- (c)69
- (d)83
We are given a set of n distinct elements and an unlabeled binary tree with n nodes. In how many ways can we populate the tree with the given set so that it becomes a binary search tree?
- (a)0
- (b)1
- (c)n!
- (d)1 * 2nCn n+1
In a binary tree with n nodes, every node has an odd number of descendants. Every node is considered to be its own descendant. What is the number of nodes in the tree that have exactly one child?
You are given the postorder traversal, P, of a binary search tree on the n elements 1,2,..., n. You have to determine the unique binary search tree that has P as its postorder traversal. What is the time complexity of the most efficient algorithm for doing this?
- (a)O(logn)
- (b)O(n)
- (c)O(nlogn)
- (d)none of the above, as the tree cannot be uniquely determined. DATA STRUCTURE
The maximum number of binary trees that can be formed with three unlabeled nodes is:
- (a)1
- (b)5
- (c)4
- (d)3
How many distinct binary search trees can be created out of 4 distinct keys?
- (a)5
- (b)14
- (c)24
- (d)42
Self-check
Question 1
Keys 15, 10, 20, 8, 12, 25, 18 are inserted in this order into an empty BST. What is the height, and what is the inorder successor of 15?
Show answerHide
15 is the root; 10 and 20 are its children; 8 and 12 are under 10; 25 and 18 are under 20 (18 left, 25 right). The height is 2. The successor of 15 is the minimum of its right subtree, 18.
Question 2
A BST on the keys 1 to 7 is a chain (each node has only a right child). How many insertion orders create exactly this tree?
Show answerHide
Exactly one: the sorted order (formula ).
Question 3
A search for 55 in a BST examines the keys 40, 70, 50, 60, 55 in that order. Is this possible?
Show answerHide
Check the bounds. 40 < 55, go right (all later keys ). 70 > 55, go left (all later ). 50 < 55, go right (all later ). 60 > 55, go left (all later ). 55 fits: . Every step is consistent, so yes, it is possible.
Question 4
Delete the root 50 from the tree with preorder 50 30 20 40 70 60 80 using the inorder successor. What is the new preorder?
Show answerHide
The tree is perfect. The successor of 50 is the minimum of the right subtree, 60. Copy 60 into the root and delete the old 60 (a leaf). New preorder: 60 30 20 40 70 80.
Question 5
What is the worst-case time to search a BST with keys, and for which input does it occur?
Show answerHide
, when the tree is a chain, for example when the keys were inserted in sorted (or reverse sorted) order.
Revision card
One-screen revision
- Left subtree keys node right subtree keys (whole subtrees). Inorder sorted.
- Search, insert, delete ; from to . Sorted insertion chain.
- Insert: new leaf at the end of the search path.
- Delete: leaf; one child (child moves up); two children (copy successor or predecessor, delete that node).
- Min leftmost, max rightmost; successor = min of right subtree or lowest left-turn ancestor.
- Preorder (with BST order) rebuilds the tree. BSTs, orders.
- Validate with bounds or by strictly increasing inorder, not by comparing only with children.
References
- ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms, 3rd ed., Chapter 12 Binary Search Trees (12.1 What is a binary search tree?, 12.2 Querying, 12.3 Insertion and deletion, 12.4 Randomly built binary search trees)
- ·Kernighan and Ritchie, The C Programming Language, 2nd ed., Section 6.5 Self-referential Structures
Verification
The three animations replay real runs of the shown insert() and delete() functions: the tree is drawn from the program's left and right pointers, the call stack is the real recursion, and the test suite checks that insertion keeps the inorder sorted, that sorted insertion gives a chain of height n - 1, and that the three deletions leave a valid search tree with the stated keys. All C programs on this page are compiled with -Wall -Wextra -Werror and run by npm run verify:c, also under AddressSanitizer and UBSan (leak detection off; the examples build small trees and do not free them). The number of insertion orders per shape and the number of shapes were checked by trying every insertion order of 1..n for n up to 7 in a one-off script that is not part of the repository. The textbook section numbers were written from memory and have NOT been checked against the books. The prose and the GATE-pattern claims have NOT yet had a human textbook check (the independent second pass is described at the end of these notes). SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand (long C programs were not re-read line by line: they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose). No defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)