Binary search tree BST
One rule added to a binary tree: left < node < right, and it must hold for the whole subtree, not only for the two children. Each comparison then drops an entire subtree from the search. It is binary search, growing on a tree you can insert into and delete from at any time.
Why it exists: fast lookup and fast update at the same time
A sorted array and a linked list each fail at one of the two. A balanced BST does both in O(log n).
Look at what the two earlier structures are good at. A sorted array searches very quickly: binary search halves the range each time, O(log n). But inserting a new value means making room for it, and every element to its right moves one position, O(n). A linked list is the opposite. Once you already hold the position, linking a node in changes two pointers, O(1). Finding a value, though, means walking from the head, O(n). A linked list cannot even run binary search, because it has no O(1) random access: the step "jump to the middle" itself costs O(n).
| Structure | Search | Insert / delete | The reason |
|---|---|---|---|
| Sorted array | O(log n) (binary search) | O(n) (shifting) | Contiguous memory: any change moves the other elements |
| Linked list | O(n) (walk from the head) | O(1) (once you hold the position) | No random access, so there is no way to jump to the middle |
| BST (while balanced) | O(h) | O(h) | Has to be kept from degenerating (§05) |
The idea of a BST is to freeze the decisions of binary search into the shape of a tree. Think of a library. The sign at the entrance says "A–M left, N–Z right". Inside the left wing another sign says "A–F left, G–M right". Each sign you read removes a large part of the building from your search. Every node of a BST is one of those signs. And because the nodes are connected by pointers (chapter 3), adding a new sign moves nothing that is already there.
For any node: every node in its left subtree is smaller than it, and every node in its right subtree is larger. The rule is about the whole subtree, not about the two children. That distinction is the most common misunderstanding in this chapter, and LC 98 in §06 is built to expose it.
At each node you ask one question, smaller or larger, and the whole subtree on the other side is dropped. The number of comparisons equals the number of nodes on the path you walk, which is at most h + 1 (a path of h edges touches h + 1 nodes). When the tree is balanced, each step removes about half the remaining nodes.
Insert: follow the search path to an empty slot, attach the new node, change one pointer. Delete: at most one path is touched. Nothing is shifted along an array. This is the behavior inherited from the linked list.
A dictionary you can still write in
A printed dictionary is sorted but fixed: adding a word means printing a new edition. A hash table is the opposite: reads and writes are very fast, but the entries come out in no useful order. A BST is a dictionary with loose pages. You can add a page at any time, and reading it in order (in-order traversal) still gives you sorted entries. Whenever a requirement contains both changing data and order, a tree structure is usually the answer.
The property: in-order traversal gives sorted keys
In-order output is ascending, search is one path down — then grow a lopsided tree yourself
The previous chapter covered four traversals. For a BST one of them matters more than the rest: in-order traversal (left → node → right) always outputs the keys in ascending order. Why? Draw the tree with each node above its own position on the line below. The rule puts every node of the left subtree left of the root and every node of the right subtree right of it, so the horizontal position of a node is its rank in sorted order:
How is this used? Read it in both directions. To get the data out of a BST in order, traverse in order; no sorting step is needed. To check whether a tree is a BST, check that its in-order sequence is strictly increasing. To find the k-th smallest value, count to k during an in-order traversal. One property carries half of this chapter's problems.
Now search. Type a number into the lab below and press Search. It starts at the root, and after each comparison it continues on one side only. Try Insert as well: the new value always ends up in the empty slot where the search failed. Then press "Insert 1→5 in order" at least once and watch a tree that still obeys the rule but has lost all its speed.
That lopsided tree is the weak point of a BST
With sorted input every new value turns the same way, and the tree leans into a chain. The height h goes from about log n up to n − 1, and search stops removing subtrees and starts checking nodes one by one, exactly like a linked list. The rule (left smaller, right larger) guarantees correctness, not shape. Shape is the job of the balanced trees in §05.
Core operations: every one of them is O(h)
Search and insert are straightforward. Delete has three cases, drawn one by one.
| Operation | Complexity | Why |
|---|---|---|
| search | O(h) | Each comparison drops one subtree, so you walk at most one root-to-leaf path |
| insert | O(h) | A failed search, plus attaching the node in the empty slot (one pointer) |
| delete | O(h) | O(h) to locate it, then repair; in the worst case a further walk to find the successor |
| minimum / maximum | O(h) | Go left (or right) until there is no child; no comparison is needed |
| in-order traversal (the sorted sequence) | O(n) | Every node has to be visited once |
Why O(h) everywhere and not O(log n)? Because h, the height of the tree, is a variable. As in chapter 7, height is the number of edges on the longest root-to-leaf path, so a tree with only a root has height 0. When the tree is balanced, h = ⌊log₂n⌋, since each level can hold twice as many nodes as the one above it. When sorted input makes the tree degenerate into a chain, h = n − 1. Saying O(log n) without a condition is wrong. The complete answer is: O(h); that is log n when the tree is balanced and n in the worst case, and production code uses a red-black tree to keep h at log n.
You have already done search and insert by hand in the lab. The hard operation is delete. Removing a node leaves a hole in the tree, and the hole has to be filled without breaking the ordering. Split it by how many children the deleted node has:
Case 3 raises two questions worth answering. Why can the successor take the place? The in-order successor is the next key above the deleted one, with nothing in between. After it moves up, every node on the left is still smaller than it (it is larger than the deleted value), and every remaining node on the right is still larger than it (it was the smallest value there). The ordering holds on both sides, and no other key would do. Why is deleting the successor easy? The successor is the end of a walk left from the right child, so it has no left child. Deleting it falls into case 1 or case 2, and the recursion cannot continue for ever. The in-order predecessor (the largest value in the left subtree) works the same way, by symmetry.
Delete in one line
A leaf is removed; a node with one child is replaced by that child; a node with two children takes the successor's value, and the delete moves into the right subtree. That turns "remove a node in the middle" into "remove a node at the edge". Total cost: locate, find the successor, delete again — all of it walks downwards along one path, so O(h).
Writing one: a complete BST in about 60 lines
insert / search / delete / inorder — all three delete cases included, and it runs
The node is the TreeNode from the binary tree chapter: a value plus a pointer to each child. Of the four methods, insert and delete are written recursively, and both return the root of the repaired subtree so the caller can reattach it. This pattern — return yourself so the parent links to you again — is the standard way to modify a tree by recursion, and it avoids having to track parent pointers.
Balanced trees: AVL, red-black, and what each language gives you
A concept section. You are not asked to implement these, but you should be able to say why they exist and which one to use.
The lab in §02 showed it already: sorted input grows a BST into a chain. Real data is often sorted: log records written by timestamp, rows inserted by auto-increment id, word lists imported alphabetically. A plain BST will almost certainly degenerate in production. The idea behind the fix is direct: after an insert or a delete, if some part of the tree has become too deep on one side, rotate it back. Balanced trees differ only in how they define "too deep" and how far they let it go before acting.
AVL tree: no imbalance allowed
AVL (1962, named after its inventors Adelson-Velsky and Landis) gives every node a balance factor: the height of the left subtree minus the height of the right subtree. It requires that factor to stay in {-1, 0, 1} for every node. As soon as an insert pushes some node to |BF| = 2, a rotation repairs it. Here is the smallest example, inserting 3, 2, 1 in that order:
A real AVL also handles the "left-right" and "right-left" shapes, which need two rotations. The idea is the same: bring the middle of the three values up to be the root. AVL is the strict option. Its height stays close to log n, so lookups are as fast as they can be. The price is paid on writes.
Red-black tree: balanced enough, not perfect
A red-black tree takes a different route. Instead of watching the height difference, it gives each node a color and keeps five rules:
- 1. Every node is either red or black.
- 2. The root is black.
- 3. Empty positions (the null children) count as black.
- 4. The children of a red node must be black, so two reds can never be adjacent.
- 5. From any node, every path down to an empty position contains the same number of black nodes.
Together these give a neat result. The longest path can only alternate red and black, the shortest path can be all black, and both contain the same number of black nodes by rule 5. Therefore the longest path is at most twice the shortest, and the height stays in O(log n). The tree is allowed to be somewhat uneven, and in exchange each update needs only a small fixed number of rotations: at most 2 for an insert and at most 3 for a delete. An AVL insert also needs at most one rotation, but an AVL delete may have to rebalance at every level on the way back to the root. Not perfectly balanced, just balanced enough — that is an engineering trade-off, and it is why the red-black tree ended up in the standard libraries. Use AVL when reads dominate; use red-black when reads and writes are mixed.
Why red and black?
Guibas and Sedgewick published the structure in 1978 at Xerox PARC. Sedgewick has said the colors came from the lab printer: it could print red and black, and red made the special nodes easiest to pick out in the paper. The ink available in one machine set the color scheme of textbooks for the next fifty years.
In practice: ordered containers in three languages
One question decides whether you want a tree at all: do you need order? For a plain lookup — is this key present, what is its value — a hash table wins, because it is O(1) on average and a tree is O(log n). You choose a balanced BST when the requirement mentions range queries, the nearest key above or below a value, or iterating in key order. A hash table cannot answer any of those without reading everything and sorting it.
| Language | Ordered container | What it really is | When to use it |
|---|---|---|---|
| Java | TreeMap / TreeSet | Red-black tree | Ordered iteration, floorKey / ceilingKey (nearest key), and subMap range queries |
| Python | None built in (third-party sortedcontainers) | A list of short sorted blocks, not a tree | SortedList / SortedDict, already installed in the LeetCode environment |
| JavaScript | None built in | — | Map only keeps insertion order. Use a sorted array with binary search, or write the tree yourself |
bisect.insort searches quickly but still inserts in O(n), because a list is a dynamic array (chapter 1). SortedList is much faster on writes because it stores the values in many short blocks, so an insert only shifts one short block. It is not a balanced tree, and its insert is not O(log n) in theory, but for interview and contest workloads it is the practical choice.In production: why do database indexes use a B+ tree instead of a red-black tree?
The index in MySQL (InnoDB) is a B+ tree. It is also an ordered tree, so why not the red-black tree that wins in memory? Because the setting is different. The data lives on disk, and disk is read one page at a time, usually 16KB, so reading 1 byte costs almost the same as reading 16KB. A red-black tree stores one key per node and has height about log₂n, so for a million rows one lookup walks a path of roughly 20 nodes, which means up to 20 disk reads. A B+ tree instead fills each node with a whole page of several hundred keys. With a few hundred children per node, a million rows fit in 3 or 4 levels: a short, wide tree means very few reads. A B+ tree also keeps all the data in the leaf level and links the leaves together, so a range scan (WHERE id BETWEEN …) follows that list instead of jumping around the tree. In one sentence: the red-black tree is designed for random access in memory, the B+ tree for page-sized reads from disk. Structures are not better or worse; they fit a particular medium.
Patterns and walkthroughs: one key opens most BST questions
★ Interview coreWhen you see a BST, say it to yourself: in-order is sorted, and one comparison drops one subtree. Three problems, frame by frame.
Most BST problems fall into three patterns. First, use in-order = sorted (k-th smallest, minimum difference, validation, recovery). Second, use the comparison to drop a subtree (search, insert, delete, lowest common ancestor, pruning a range sum). Third, build in reverse (turn sorted data into a balanced BST). The three walkthroughs below take one pattern each.
LC 98 · Validate Binary Search Tree
MEDIUMThe task: decide whether a binary tree is a valid BST. The trap: almost everyone first writes "check that each node is larger than its left child and smaller than its right child". That is wrong. The rule covers the whole subtree, not one parent and its children. Here is the counterexample built to break it:
lo < node.val < hi is a Python feature, and float("±inf") removes any worry about integer bounds.Complexity and follow-up questions
Each node is checked once: O(n) time, O(h) space for the recursion stack. Follow-up one, "is there another solution?" — yes: traverse in order and check that the sequence strictly increases (keep prev, and fail as soon as cur ≤ prev). That is the property from §02, and you should know both. Follow-up two, "why is the range open at both ends?" — because the problem requires strictly smaller and strictly larger, so an equal value is also a violation; duplicates are not allowed.
LC 230 · Kth Smallest Element in a BST
MEDIUMThe task: return the k-th smallest value in a BST. The brute force: traverse in any order, collect every value, sort, take the k-th. That is O(n log n) and uses nothing about the BST. Why in-order: the sorting was already done when the tree was built. In-order traversal is the values in ascending order, so you only have to count as you go and stop at k:
Complexity and follow-up questions
Time O(h + k): O(h) to reach the leftmost node, then k values are produced. Space O(h). The classic follow-up: "what if inserts and deletes are frequent and you also query the k-th smallest often?" — store the size of the left subtree in every node. To query: if k ≤ leftSize go left; if k = leftSize + 1 this node is the answer; otherwise go right with k − leftSize − 1. That is O(h) per query, and the counts are updated during insert and delete. It is binary search performed on the tree itself.
LC 108 · Convert Sorted Array to Binary Search Tree
EASYThe task: turn an ascending array into a height-balanced BST. What not to do: insert the elements one by one. The input is sorted, and the lab in §02 showed what that produces: a chain. The solution: balance means the two sides hold almost the same number of nodes, and in a sorted array it is obvious which value belongs in the middle. Take the midpoint as the root, and build the two halves recursively.
Splitting at the midpoint leaves two halves whose lengths differ by at most 1. Every level of the recursion preserves that, so the two subtrees of any node differ by at most one node, and their heights differ by at most 1. What you are really building is the decision tree of binary search: mid is the root, the two halves are the subtrees, and every path binary search could take becomes a path in the tree.
Complexity and follow-up questions
Each element becomes a root exactly once: O(n) time, O(log n) space for the recursion stack. Follow-up: "what if the input is a sorted linked list?" (LC 109). Finding the middle of a linked list costs O(n), so there are two approaches: use the slow and fast pointers to find the middle each time, which totals O(n log n); or build in in-order sequence — count the length first, then recurse over the index range while consuming the list nodes in order, which stays O(n). Explaining that step finishes this group of problems.
Problem set: 9 BST questions
Interview regularsOrdered as core operations, then using the sorted property, then changing the structure. Think for 30 seconds before opening the hint.
Chapter quiz
✎ QuizGet all 7 right to light up this chapter
A tree: the root is 10, its left child is 5 and its right child is 15; the left child of 15 is 6 and its right child is 20. Every parent-child pair is fine (5<10, 15>10, 6<15, 20>15). Is it a BST?
Which traversal of a BST outputs the keys in ascending order? (Write the name of the traversal.)
You insert values one by one into an empty BST. Which order makes it degenerate into a chain?
What data structure backs TreeMap and TreeSet in Java?
In an interview, what is the most precise way to state the time complexity of search in a BST?
You delete a node that has two children. Which node takes its place?
In which situations should you choose TreeMap (an ordered map) over HashMap? (Select all that apply.)
- One rule defines everything: left < node < right, for the whole subtree. When you validate or use a BST, remember that the constraints of the ancestors are passed down (the range method in LC 98).
- In-order traversal gives the keys in sorted order. K-th smallest, minimum difference, validation, recovery — most BST problems reduce to this one property.
- Search, insert, and delete are all O(h), with the height h counted in edges: h = ⌊log₂n⌋ while the tree is balanced, and h = n − 1 after sorted input degenerates it. State the complexity with h, or state the condition.
- Three delete cases: remove a leaf; replace a node that has one child by that child; for two children, copy the in-order successor and delete it from the right subtree. The successor has no left child, so the hard case turns into an easy one.
- A BST with no rebalancing is not used in production: Java has
TreeMap/TreeSet(red-black, balanced enough), Python has the third-partysortedcontainers, JavaScript has nothing built in. On disk, database indexes use a B+ tree: short and wide to cut the number of reads, with linked leaves for range scans. - Choose between a hash table and a BST by one question: do you need order? Plain lookups go to the hash table. Range queries, nearest-key lookups, and iteration in key order go to the ordered structure.