Yasir Explains/Algorithms/M-way Trees, B-Trees & Amortized Analysis/B-Tree Insertion & Node Splitting
M-way Trees, B-Trees & Amortized Analysis

B-Tree Insertion & Node Splitting

On this page

The Core IdeaHow a Split WorksExample: Insert into a B-tree with t = 2Example: Build a B-tree with t = 3PseudocodeComplexity
M-way Trees, B-Trees & Amortized Analysis

B-Tree Insertion & Node Splitting

How B-trees stay balanced while growing: insert at a leaf, and split full nodes so the median climbs toward the root.

The Core Idea

A new key always ends up in a leaf — B-trees never insert into internal nodes directly. The only complication is what to do when the target leaf (or a node on the path to it) is already full (has 2t − 1 keys). The answer is splitting.

Node split. A full node with 2t − 1 keys is divided into two nodes of t − 1 keys each, and its median key moves up into the parent as a separator. The parent gains one key and one child.

Because the median rises into the parent, splitting is the mechanism by which a B-tree grows upward. When the split reaches an already-full root, the root itself splits and a brand-new root is created — that is the only way a B-tree increases its height, which is why all leaves always stay at the same level.

How a Split Works

Take a full node (t = 3, so 5 keys) [40, 50, 60, 70, 80] whose parent needs to gain a key here. The median is the middle key, at index t − 1 = 2, which is 60:

Example
Before (full, 5 keys): [ 40 | 50 | 60 | 70 | 80 ]
Split around median 60:
60 ← median rises to parent
/ \
[ 40 | 50 ] [ 70 | 80 ] ← two half-nodes (t−1 = 2 keys each)

The left half keeps keys before the median, the right half keeps keys after it, and 60 is inserted into the parent between the two child pointers. Each half now has exactly t − 1 = 2 keys — the legal minimum — so both halves are valid B-tree nodes.

CLRS's trick — split on the way down. Rather than insert first and fix overflow afterward, the standard algorithm splits any full node the moment it is encountered while descending. This guarantees that when we finally reach the leaf, its parent is not full and can absorb a rising median — so a single downward pass suffices, with no need to walk back up.

Example: Insert into a B-tree with t = 2

Insert 10, 20, 5, 6, 12, 30, 7, 17, 3, 25, 40 into an empty B-tree of minimum degree t = 2 (so max 2t − 1 = 3 keys; a node is full at 3 keys). We split full nodes on the way down.

Example
10, 20, 5 → [5 | 10 | 20] ← root now full (3 keys)
insert 6 → root is full → SPLIT root ← ★ split (height 1→2)
median 10 rises to a new root
[10]
/ \
[5|6] [20]
insert 12 → [10] / ([5|6] , [12|20])
insert 30 → [10] / ([5|6] , [12|20|30])
insert 7 → [10] / ([5|6|7] , [12|20|30])
insert 17 → right child [12|20|30] is full → SPLIT ← ★ split
median 20 rises → root becomes [10|20]
[10 | 20]
/ | \
[5|6|7] [12|17] [30]
insert 3 → left child [5|6|7] is full → SPLIT ← ★ split
median 6 rises → root becomes [6|10|20]
[6 | 10 | 20]
/ | | \
[3|5] [7] [12|17] [30] ← root now full
insert 25 → root [6|10|20] is full → SPLIT root ← ★ split (height 2→3)
median 10 rises → new root [10]
[10]
/ \
[6] [20]
/ \ / \
[3|5] [7] [12|17] [25|30]
insert 40 → lands in leaf [25|30] → [25|30|40]

Splitting occurs at the insertions of 6, 17, 3, and 25. The splits at 6 and 25 are root splits — the only two operations that increased the tree's height. Here is the final tree:

Example: Build a B-tree with t = 3

Now insert 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150 into an empty B-tree of minimum degree t = 3 (max 5 keys; median is the 3rd key of a full node). We keep this tree for the deletion topic that follows.

Because the keys arrive in sorted order, every split happens on the right spine — but the tree still stays perfectly balanced:

Example
10..50 → [10|20|30|40|50] ← full
insert 60 → SPLIT root, median 30 rises ← ★ split (height 1→2)
[30] / ([10|20] , [40|50|60])
...70, 80 → right leaf fills to [40|50|60|70|80]
insert 90 → right leaf full → SPLIT, median 60 rises ← ★ split
[30|60] / ([10|20],[40|50],[70|80|90])
...100,110→ [70|80|90|100|110] fills
insert 120→ full leaf → SPLIT, median 90 rises ← ★ split
[30|60|90] / (…,[70|80],[100|110|120])
...130,140→ [100|110|120|130|140] fills
insert 150→ full leaf → SPLIT, median 120 rises ← ★ split
[30|60|90|120]

Final tree after all 15 insertions (height 1 — just root + leaves):

Notice how a large minimum degree keeps the tree astonishingly flat: 15 keys fit in just two levels, and the root's 4 keys point to 5 leaves.

Pseudocode

The insertion is a single top-down pass. B-TREE-SPLIT-CHILD divides a full child; B-TREE-INSERT-NONFULL places the key, splitting full children before descending so it is never blocked at a leaf.

1B-TREE-INSERT(T, k)
2 r = T.root
3 if r.n == 2t − 1 // root is full
4 s = new node; s.leaf = false
5 s.child[0] = r; T.root = s
6 B-TREE-SPLIT-CHILD(s, 0) // grow height by 1
7 B-TREE-INSERT-NONFULL(s, k)
8 else
9 B-TREE-INSERT-NONFULL(r, k)
10
11B-TREE-INSERT-NONFULL(x, k)
12 i = x.n − 1
13 if x.leaf
14 while i >= 0 and k < x.key[i]
15 x.key[i+1] = x.key[i]; i = i − 1
16 x.key[i+1] = k; x.n = x.n + 1 // drop key into sorted slot
17 else
18 while i >= 0 and k < x.key[i]: i = i − 1
19 i = i + 1
20 if x.child[i].n == 2t − 1 // child full → split before descending
21 B-TREE-SPLIT-CHILD(x, i)
22 if k > x.key[i]: i = i + 1
23 B-TREE-INSERT-NONFULL(x.child[i], k)

Complexity

Insertion touches the nodes on one root-to-leaf path and may split a node at each level, but every split is O(t) work. With height O(log_t n):

  • Comparisons / CPU work: O(t · log_t n).
  • Disk accesses: O(log_t n) reads down the path plus O(log_t n) writes for split nodes.

Crucially, most inserts cause no split at all — a split only happens when a node is full. This "rare, bounded restructuring" is exactly what makes the amortized cost so low, a theme we formalize later in this chapter.

Complexity Analysis

Time Complexity

O(t · log_t n)

Space Complexity

O(log_t n)

One downward pass; O(log_t n) disk accesses. Most inserts split nothing.

Growth Rate Comparison

n (input size)O(1)O(log n)O(n)O(n log n)O(n²)