Disk-Optimized Balanced Tree | Order-m | Database Index | O(log_m n)
Standard Undergraduate
§1 — Interactive Simulation
Ready — Press Play or Next to begin.
Speed:
0
Operations
0
Comparisons
0 / 0
Step
—
n
B-Tree of minimum degree t (order 2t):
Every non-root node has ≥ t-1 keys and ≤ 2t-1 keys
Every non-root non-leaf has ≥ t children
Every leaf has same depth (perfectly balanced)
Root: 1 to 2t-1 keys
SEARCH(k): like BST but with m-way branching
INSERT: find leaf, insert, split if overflow (2t keys)
Proactively split full nodes on the way down
DELETE: find and remove, merge/redistribute if underflow
§2 — The Idea, Step by Step
Start — a phone book with tabbed pages. Imagine a giant phone book, but each page holds a whole block of names already in order, plus little tabs telling you which page to flip to next. To find "Khan," you don't read every page — you glance at the tabs, jump to the right block, and scan just that one. A B-tree is exactly this: a tree of fat "pages," each holding many sorted keys and signposts to the pages below. Few flips, fast find.
Build — why the pages are fat. The slow part of a database isn't comparing numbers; it's fetching a page from disk, which is roughly 100,000× slower than reading memory and always grabs a whole block (say 16 kB) at once. So we pack hundreds of keys into each node and read them in a single fetch. A node with $m$ children stores $m-1$ keys in sorted order, and the child sitting between two keys holds every value that falls between them. If each node carries about 100 keys, one node covers 100 records, its children cover $100\times100=10{,}000$, and three levels already reach a million. Worked number: with branching $\sim 100$, a million records sit only $\log_{100}(10^6)=3$ fetches deep.
Deepen — the precise rule. Formally, a B-tree of minimum degree $t$ keeps between $t-1$ and $2t-1$ keys in every non-root node (the root may hold fewer), and every leaf lies at the same depth, so the tree stays perfectly height-balanced. Its height obeys $h \le \log_t\!\frac{n+1}{2}$, giving search, insert, and delete in $O(\log_t n)$ disk accesses. Inserting splits a full node — its median key rises into the parent — while deleting borrows from or merges with a sibling to avoid underflow. In the sim, the n slider sets how many keys you feed in, and the preset menu changes the order they arrive in.
Try this in the sim above. Step through with Next while reading the pseudocode panel beside the animation — it states the genuine B-tree invariants ($\ge t-1$ keys per non-root node, split a full node on the way down). Switch the preset between Sorted and Random and watch how the per-step messages change as the keys are processed in different orders. Then slide n from 4 up to 16 and press Reset to load more keys, and use First / Last to jump straight to the ends of the trace.
§3 — Algorithm Analysis & Derivation
Symbol
Meaning
n
Input size
T(n)
Time complexity function
W(n)
Worst-case complexity
A(n)
Average / amortized complexity
Φ(S)
Potential function of data structure state S
α(n)
Inverse Ackermann function (~constant)
Step 1 — Motivation: Disk I/O. In databases, data lives on disk. Disk read/write is 100,000× slower than RAM. Each disk read fetches a 'page' (4KB-16KB). B-tree node = one disk page, holding many keys (t ~ 50-1000). Tree height: log_t(n). For n=10^9, t=500: height ≤ 4. Only 4 disk reads to find any record!
Step 2 — Structure. Minimum degree t. Node properties: each non-root node has t-1 to 2t-1 keys. Children = keys+1. All leaves at same depth (height-balanced). Keys within a node are sorted. For a node with keys k_1,...,k_m and children C_0,...,C_m: C_i contains all keys between k_i and k_{i+1}.
Step 3 — Search O(log_t n) = O(log n / log t). At each node: binary search for key among up to 2t-1 keys O(log t), then recurse into child. Height: h ≤ log_t((n+1)/2). Total: O(h · log t) = O(log n). Disk accesses: O(h) = O(log_t n) — each level = one disk read.
Step 4 — Insert with Proactive Splitting. Search for insertion leaf. If any node on the path is full (2t-1 keys): split it before descending. Splitting: median key moves up to parent, node splits into two nodes of t-1 keys each. This ensures parent always has room for the new key. O(h) disk writes per insert.
Step 5 — Delete with Merge/Redistribute. Three cases: (1) Key in leaf with t keys — simple delete. (2) Key in internal node — replace with in-order successor/predecessor (from a child with t keys). (3) Key in node with t-1 keys — first redistribute from sibling (if sibling has t keys) or merge with sibling. O(h) disk accesses.
Step 6 — B+ Tree (Standard in Databases). B+ tree: all data stored in leaves. Internal nodes hold only keys for routing. Leaves linked in a sorted linked list — enables efficient range scans. MySQL InnoDB, PostgreSQL, SQLite, Oracle: all use B+ trees. B+ tree leaf scan: O(k) for k consecutive records — no tree traversal needed.
§4 — Frequently Asked Questions
Why is B-tree better than a balanced BST for databases? concept+
BST has height O(log_2 n). Each node = one key = one (small) memory allocation. For n=10^9: height ~ 30, each step follows a pointer (cache miss or disk read). B-tree with t=500: height ~ 4, each node has 1000 keys in one disk page. 4 disk reads vs 30 disk reads — 7.5× fewer I/Os.
What is the difference between B-tree and B+ tree? concept+
B-tree: data (records) stored in ALL nodes (internal + leaf). B+ tree: data stored ONLY in leaves; internal nodes hold only routing keys. B+ tree advantage: all leaves linked (efficient range scans), internal nodes can hold more keys (no data payload), simpler deletion. Most databases use B+ trees.
How does InnoDB (MySQL) use B+ trees? applied+
Each table has a clustered B+ tree index on the primary key — leaf nodes store actual row data. Secondary indexes store primary key values (not row pointers). Lookup by secondary index: find PK in secondary index leaf, then lookup PK in clustered index (two B+ tree traversals). Range queries on PK: single traversal + linked leaf scan.
Why is height O(log_t n) and what is t in practice? complex+
Minimum keys per non-root node: t-1. Minimum children: t. Minimum nodes at level i: 2·t^{i-1}. Total nodes: ≥ 1 + ∑_{i=1}^h 2t^{i-1}. Solving: h ≤ log_t((n+1)/2). In practice: disk page 16KB, key+pointer ~16 bytes, t ~ 500. Height 3-4 for billion rows.
What is a 2-3-4 tree and how does it relate? nonobv+
2-3-4 tree is a B-tree with t=2: nodes have 1-3 keys, 2-4 children. Height O(log_4 n) ≤ O(log n). 2-3-4 trees are isomorphic to red-black trees (2-node=black, 3-node=red-black, 4-node=red-black-red). B-trees generalize 2-3-4 trees to arbitrary t.
How does LSM-tree differ from B-tree for write-heavy workloads? deep+
Log-Structured Merge tree (LSM): writes go to in-memory buffer (memtable), periodically flushed to disk as sorted runs, background compaction merges runs. Write: O(1) to memtable vs O(log n) disk writes for B-tree. Read: must check multiple levels vs O(1) B-tree disk reads. LSM used by: LevelDB, RocksDB, Cassandra, HBase — all write-heavy workloads.
§5 — Misconceptions & Implementation Errors
Conceptual Misconceptions
✗ B-tree nodes must be fully filled before splitting.
✓ Proactive splitting: split FULL nodes (2t-1 keys) on the WAY DOWN during insert, before they overflow. This ensures the parent always has room — no need to backtrack up the tree.
✗ All B-tree variants store data in all nodes.
✓ B-tree: data in all nodes. B+ tree: data ONLY in leaves (internal nodes = routing keys only). B* tree: ensures 2/3 full instead of 1/2. Most databases use B+ trees.
✗ B-tree height is always exactly log_2 n.
✓ Height is O(log_t n) = O(log n / log t). For t=500: height ~ log n / 9, far less than log_2 n. This is the key advantage over BSTs for disk-based storage.
Implementation Errors
✗ Splitting a node during insert always increases tree height.
✓ Splitting a non-root node does NOT increase tree height — it splits one node into two siblings at the same level. Height increases ONLY when the root is split, creating a new root one level up.
✗ Forgetting to update parent's key/child pointers after a split.
✓ When splitting a child node: must update parent to include the median key and point to the two new child nodes. Forgetting corrupts the tree structure.
✗ Confusing minimum degree t with maximum number of keys 2t-1.
✓ Min degree t: minimum children = t, minimum keys = t-1, maximum keys = 2t-1. A 'B-tree of order m' sometimes means maximum children = m = 2t, so t = m/2. Clarify: is t the minimum degree or half the maximum degree?