Binary Search Trees · 6 of 22
Populate Inorder successor of all nodes
Give every spot in a tree an extra arrow that points to the next bigger number, by visiting the numbers from biggest to smallest.
- Simple way
- time O(n)
room O(n) - Clever way
- time O(n)
room O(h)
The problem, simply
Every spot in the tree has the usual left and right arrows, and one more empty arrow called next. Fill in next so it points to the spot that comes right after this one in sorted order (its inorder successor). The biggest spot's next stays null.
When you are done, you can start at the smallest spot and follow next arrows to visit every number in sorted order, like a chain.
In the classic version the tree is any binary tree and "sorted order" means the left, me, right reading order. For a BST, that order is exactly smallest to biggest.
A story to remember it
Picture it
A BST of 6 numbers. The chain under it is what the next arrows should spell: 2, 5, 7, 10, 15, 20, then null.
Examples
A normal tree
- In
[10, 5, 15, 2, 7, null, 20]- Out
2>5, 5>7, 7>10, 10>15, 15>20, 20>null
7 is the biggest on the left side, so its next jumps up to the top, 10.
A stick going left
- In
[3, 2, null, 1]- Out
1>2, 2>3, 3>null
Each child points to its parent.
A stick going right
- In
[1, null, 2, null, 3]- Out
1>2, 2>3, 3>null
Each parent points to its child.
One spot
- In
[4]- Out
4>null
Nothing comes after it.
Way 1: the simple way
Write every spot into a list in left, me, right order. Then walk the list and point each spot to the one after it.
- Visit left side, the spot, right side, and add each spot to a list.
- For
ifrom0to the second last, setlist[i].next = list[i + 1]. - The last one's
nextisnull.
import java.util.*;
class Solution {
public void populateNext(Node root) {
List<Node> order = new ArrayList<>();
inorder(root, order);
for (int i = 0; i + 1 < order.size(); i++) {
order.get(i).next = order.get(i + 1); // point to the one after me
}
if (!order.isEmpty()) order.get(order.size() - 1).next = null;
}
private void inorder(Node node, List<Node> order) {
if (node == null) return;
inorder(node.left, order);
order.add(node);
inorder(node.right, order);
}
}How long it takes: every spot twice, O(n). Extra room: the list of all spots, O(n).
Way 2: the clever way
Visit in the opposite order: right side, the spot, left side. That reads the numbers from biggest to smallest. Keep one finger, prev, on the spot you visited last.
- Start with
prev = null. - Visit the right side first.
- At the spot: set
spot.next = prev, then moveprevto this spot. - Visit the left side.
The spot you visited just before is the next bigger one, which is exactly the successor. The chain grows from its end back to its start.
Biggest to smallest, each spot points at the one before
01 / 07
Go right as far as possible, to 20. It is the biggest, and prev is null, so 20.next = null. Move prev to 20.
- prev
- null
- looking now
- done
- answer
- path
- just added
class Solution {
private Node prev; // the spot visited just before
public void populateNext(Node root) {
prev = null;
fill(root);
}
private void fill(Node node) {
if (node == null) return;
fill(node.right); // bigger numbers first
node.next = prev; // the last one seen is my successor
prev = node;
fill(node.left); // then smaller numbers
}
}How long it takes: every spot once, O(n). Extra room: no list, only the recursion, O(h).
Another way: Morris walk with no extra room
To get O(1) extra room, walk left, me, right without a stack. Before going down to the left side, find the biggest spot on that side and point its right arrow back up to the current spot, like leaving a rope to climb back. When you come back by the rope, remove it. In this forward order, link prev.next = curr.
class Solution {
public void populateNext(Node root) {
Node curr = root, prev = null;
while (curr != null) {
if (curr.left == null) {
if (prev != null) prev.next = curr; // visit
prev = curr;
curr = curr.right;
} else {
Node rope = curr.left;
while (rope.right != null && rope.right != curr) rope = rope.right;
if (rope.right == null) { // first time here: tie the rope
rope.right = curr;
curr = curr.left;
} else { // came back by the rope: untie it
rope.right = null;
if (prev != null) prev.next = curr;
prev = curr;
curr = curr.right;
}
}
}
if (prev != null) prev.next = null;
}
}Still O(n) time (each rope is walked a constant number of times), and O(1) extra room. The tree is put back exactly as it was.
Watch out for
- Visiting left first while setting
node.next = prev. That points each spot at its predecessor, the wrong way round. Left-first order needsprev.next = nodeinstead. - Forgetting to reset
prevbetween calls. - The biggest spot's
nextmust benull. Both ways do this for free.
What interviewers ask next
- Populate the predecessor arrow instead. Visit left, me, right, and set
node.prev = prev. - Connect each spot to the spot on its right in the same row (LeetCode 116 and 117). That is a different "next": use the row by row walk, or reuse the already linked row above to link the row below in
O(1)room. - Turn the BST into a sorted doubly linked list in place. Same walk, but reuse
leftas "previous" andrightas "next". - Why does the Morris walk not break the tree? Every rope is tied once and untied once before the walk leaves that part.
Remember this
Grown-up words: the next bigger kid is the inorder successor, right, me, left is a reverse inorder traversal, the finger on the last visited spot is a prev pointer, and the rope trick is a Morris traversal using threaded pointers.