Populating Next Right Pointers in Each Node II
Description
Every node of a binary tree has a next pointer as well as its two children, and every next starts out empty. Point each node's next at the node to its right on the same level. The last node of each level keeps an empty next. Return the root.
The tree need not be full: any node can be missing a child. To show the answer, each level is read from its leftmost node along the next pointers, and the levels are listed top down.
Examples
Example 1
Input: root = [1, 2, 3, 4, 5, null, 7]
Output: [[1],[2,3],[4,5,7]]
Explanation: 5 and 7 are cousins: 5's next is 7, across the gap where 3 has no left child.
Example 2
Input: root = []
Output: []
Explanation: An empty tree has no levels.
Example 3
Input: root = [1, 2, 3, 4, null, null, 5]
Output: [[1],[2,3],[4,5]]
Explanation: 2 has only a left child and 3 only a right one, and 4's next is still 5.
Constraints
- The number of nodes in the tree is in the range [0, 6000].
- −100 ≤ Node.val ≤ 100
- Follow-up: use only constant extra space. The recursion stack doesn't count as extra space here.
Editorial
Brute Force
Intuition
The nodes of one level, left to right, are exactly what a level-order walk takes from its queue in one round, if each round takes as many nodes as the queue holds when the round starts. So walk the tree level by level, and within a round point each node at the one taken after it. The last node of the round keeps an empty next.
Step-by-Step Explanation
Let's trace root = [1, 2, 3, 4, 5, null, 7]:
- Level 0 is just 1, so it ends the level. Its children, 2, 3, join the queue.
- Level 1 is 2, 3. 2's next is 3, and 3 ends the level. Their children, 4, 5, 7, join the queue.
- Level 2 is 4, 5, 7. 4's next is 5, 5's next is 7, and 7 ends the level. They have no children, so the queue is empty.
One Level per Round of the Queue — Indigo is the level being linked; each badge shows where a node's next points. Under the tree, the queue.
Algorithm
- Put the root in a queue.
- While the queue isn't empty: take its size, then take that many nodes, pointing each one before the last at the next one taken, and put their children in the queue.
- Return the root.
Code
JavaScript keeps each level in its own array, so no queue has to shift.
class Solution {
public:
Node* connect(Node* root) {
if (root == nullptr) return root;
queue<Node*> q;
q.push(root);
while (!q.empty()) {
int size = q.size(); // this level's nodes, and only them
Node* prev = nullptr;
for (int k = 0; k < size; k++) {
Node* node = q.front();
q.pop();
if (prev) prev->next = node; // the node before it on this level points to it
prev = node;
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
} // the level's last node keeps next = nullptr
}
return root;
}
};
from collections import deque
class Solution:
def connect(self, root: Optional[Node]) -> Optional[Node]:
if root is None:
return root
queue = deque([root])
while queue:
size = len(queue) # this level's nodes, and only them
prev = None
for _ in range(size):
node = queue.popleft()
if prev:
prev.next = node # the node before it on this level points to it
prev = node
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
# the level's last node keeps next = None
return root
class Solution {
public Node connect(Node root) {
if (root == null) return root;
Deque<Node> queue = new ArrayDeque<>();
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size(); // this level's nodes, and only them
Node prev = null;
for (int k = 0; k < size; k++) {
Node node = queue.poll();
if (prev != null) prev.next = node; // the node before it on this level points to it
prev = node;
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
} // the level's last node keeps next = null
}
return root;
}
}
var connect = function(root) {
if (root === null) return root;
let level = [root];
while (level.length) { // this level's nodes, and only them
const below = [];
for (let k = 0; k < level.length; k++) {
const node = level[k];
if (k > 0) level[k - 1].next = node; // the node before it on this level points to it
if (node.left) below.push(node.left);
if (node.right) below.push(node.right);
} // the level's last node keeps next = null
level = below;
}
return root;
};
Complexity Analysis
Time Complexity: O(n)
Each node goes into the queue once and comes out once.
Space Complexity: O(w)
The queue holds up to a whole level, the widest one, w nodes, which is about half the tree when it's complete.
Why This Approach Is Not Efficient
It's fast, but it needs room for a whole level. On the tests' widest tree, a complete tree of 6,000 nodes, the queue held 2,048 nodes at once, and the follow-up asks for constant extra space.
The way out is in what this problem builds. Once a level is linked, it is a list: start at its leftmost node and follow next. Walking that list visits the level's nodes in order, so their children can be linked one after another as they're met, and that links the level below. Nothing needs storing but where the walk is and the last child linked.
Optimal Approach - Walk Each Linked Level
Intuition
The root alone is a linked level. Then, level by level:
- Walk the linked level from its leftmost node,
head, alongnext. - Link each child met, left before right, after the last child linked so far,
tail. A dummy node in front of the level below means even its first child is linked the same way. - Move down: the dummy's
nextis the leftmost node of the level below, the nexthead.
Step-by-Step Explanation
Let's trace root = [1, 2, 3, 4, 5, null, 7]:
- On level 0, 1's children 2 and 3 go on the end of the level below, so
tailis now 3. - Level 0 is walked. The level below, 2, 3, is linked, and its first node, 2, is where the next walk starts.
- On level 1, 2's children 4 and 5 go on the end of the level below, so
tailis now 5. - On level 1, 3's child 7 goes on the end of the level below, so
tailis now 7. - Level 1 is walked. The level below, 4, 5, 7, is linked, and its first node, 4, is where the next walk starts.
- On level 2, 4 has no children; the walk goes on along 4's next pointer.
- On level 2, 5 has no children; the walk goes on along 5's next pointer.
- On level 2, 7 has no children, and its next is empty, so the walk of level 2 ends.
Walk a Level, Link the One Below — Indigo is the node being walked; green, the tail, the last child linked. Each badge shows where a node's next points. Under the tree, the level below as it's linked.
Algorithm
- Set
head = root. - While
headisn't empty: make a dummy node and settailto it. For eachnodefromheadalongnext: if it has a left child, settail.nextto it and movetailthere; then the same for the right child. Then setheadto the dummy'snext. - Return the root.
Code
class Solution {
public:
Node* connect(Node* root) {
Node* head = root; // the leftmost node of the level being walked
while (head != nullptr) {
Node before(0); // the level below, linked as it's found; before.next is its first node
Node* tail = &before;
for (Node* node = head; node != nullptr; node = node->next) { // along this level by its next pointers
if (node->left) { tail->next = node->left; tail = node->left; }
if (node->right) { tail->next = node->right; tail = node->right; }
}
head = before.next;
}
return root;
}
};
class Solution:
def connect(self, root: Optional[Node]) -> Optional[Node]:
head = root # the leftmost node of the level being walked
while head:
before = tail = Node(0) # the level below, linked as it's found; before.next is its first node
node = head
while node: # along this level by its next pointers
for child in (node.left, node.right):
if child:
tail.next = child
tail = child
node = node.next
head = before.next
return root
class Solution {
public Node connect(Node root) {
Node head = root; // the leftmost node of the level being walked
while (head != null) {
Node before = new Node(0); // the level below, linked as it's found; before.next is its first node
Node tail = before;
for (Node node = head; node != null; node = node.next) { // along this level by its next pointers
if (node.left != null) { tail.next = node.left; tail = node.left; }
if (node.right != null) { tail.next = node.right; tail = node.right; }
}
head = before.next;
}
return root;
}
}
var connect = function(root) {
let head = root; // the leftmost node of the level being walked
while (head !== null) {
const before = new Node(0); // the level below, linked as it's found; before.next is its first node
let tail = before;
for (let node = head; node !== null; node = node.next) { // along this level by its next pointers
if (node.left !== null) { tail.next = node.left; tail = node.left; }
if (node.right !== null) { tail.next = node.right; tail = node.right; }
}
head = before.next;
}
return root;
};
Complexity Analysis
Time Complexity: O(n)
Each node is linked once as a child and walked once as a parent.
Space Complexity: O(1)
Only head, tail, the walking node and one dummy per level.
Common Mistakes
Each of these looks right on a quick read, and the tests still reject it. The outputs below come from running the code: each level read along the next pointers it set.
Linking only siblings. node.left.next = node.right
On root = [1, 2, 3, 4, 5, null, 7] the levels read [[1],[2,3],[4,5]]; they should read [[1],[2,3],[4,5,7]]. A left child's next is its sibling, but the right child's next is a cousin, under another parent. Linking siblings alone leaves every cousin unlinked: 5's next in the first example is 7, under 3.
Looking only at the next parent. after = node.next.left or node.next.right
On root = [1, 2, 3, 4, 5, 6, 7, 8, null, null, null, null, null, null, 9] the levels read [[1],[2,3],[4,5,6,7],[8]]; they should read [[1],[2,3],[4,5,6,7],[8,9]]. When the next parent along has no children, the cousin is further right still. Looking at node.next's children alone, and not on past parents with none, leaves the link empty.
Linking the whole queue in one run. prev.next = node
On root = [1, 2, 3, 4, 5, null, 7] the levels read [[1,2],[2,3,4],[4,5,7]]; they should read [[1],[2,3],[4,5,7]]. A breadth-first queue holds the levels one after another, so linking each node to the next one in the queue runs the last node of a level on into the first of the next. Each level has to end in None: take the level's size before linking it.
Where This Pattern Is Taught
This problem practises level-order walking. It's taught in Tree Traversals: Depth-First and Breadth-First, which links the levels this same way, with no queue.