Populating Next Right Pointers in Each Node
You are given a perfect binary tree — every internal node has exactly two children and all leaves are at the same depth. Each node carries an extra pointer, next, initially null. Set every node's next to the node immediately to its right on the same level, and to null for the rightmost node of each level. The tree is modified in place and the same root is returned. An empty tree is returned unchanged.
- The number of nodes is in the range [0, 6000]
- -100 <= Node.val <= 100
- The tree is perfect: every internal node has two children, all leaves share one depth
- The recursion stack does not count against extra space in the follow-up
root = [1,2,3,4,5,6,7][1,#,2,3,#,4,5,6,7,#] — # marks the end of a levelroot = [1,2,3][1,#,2,3,#]root = [1][1,#]root = [][]The goal is to turn each level of the tree into a left-to-right chain. The level-by-level walk from Binary Tree Level Order Traversal already produces nodes in exactly that order, so a first solution writes itself: process a level, and point each node's next at whichever node the queue hands over after it, leaving the last one null.
while queue:
n = len(queue)
for i in range(n):
node = queue.popleft()
if i < n - 1:
node.next = queue[0] # the next node in this level, still queued
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)That is correct and runs in O(N) time. Its cost is the queue, which on this tree holds an entire level — up to about N/2 nodes. The real question is whether we can do it with no extra structure at all.
Here is the observation that removes the queue. Once a level has been fully stitched, that level is a linked list. You can stand on its leftmost node and walk rightwards along next pointers, reaching every node of the level in order — without a queue, because the level is now storing its own ordering.
So the algorithm becomes: use the finished level above to walk across, and while walking, stitch the level below. Each level pays for the next one.
Standing on a node curr of the finished level, there are exactly two kinds of link to create beneath it:
curr.left.next = curr.right. Easy — we hold both.curr.next already points at that next parent, so the route is curr.right.next = curr.next.left. We hop sideways on our own level, then reach down.The second link is the whole trick, and it is only available because the level we are standing on was stitched during the previous round. The root's level needs no stitching (one node, nothing to its right), which starts the induction off.
leftmost = root
while leftmost and leftmost.left: # stop when the current level has no children
curr = leftmost
while curr: # walk across the finished level
curr.left.next = curr.right # siblings
if curr.next:
curr.right.next = curr.next.left # across to the neighbouring parent
curr = curr.next # step sideways using the existing link
leftmost = leftmost.left # drop to the start of the level just stitchedcurr.left and curr.right without checking them, which is safe only because the tree is perfect — an internal node always has both children, so if leftmost.left exists then every node on that level has two children. Remove that guarantee and the code crashes or, worse, silently skips links, because the next node on the level below may be several parents away with gaps in between. The follow-up problem handles exactly that, usually with a dummy head node to build each level's chain as it goes. Notice how much a single structural promise buys: it is what turns a scan-for-the-next-available-node into a direct curr.next.left.1.next is null, so there is no crossing link. Level 1 is now the chain 2 → 3. Drop to leftmost = 2.2.next is 3, so link 5 → 3.left, which is 6 — the crossing link. Chain so far: 4 → 5 → 6.2.next to 3. At 3: link 6 → 7 (siblings). 3.next is null, so no crossing link.4.left is null, so the loop ends. Every level is stitched.The step from 2 to 3 is the one to watch: it moved horizontally along a pointer created in the previous round. Without that, reaching node 3 from node 2 would have required going back up to the root.
Each node is visited once as curr and does constant work, so O(N) time — the same as the queue version. The extra space is two pointers, O(1), down from O(W). Nothing was recomputed; we simply noticed that the output being built was itself a usable structure.
That is the idea worth extracting: when an algorithm's output is a set of links, check whether the links already written can be used to navigate while writing the rest. It is the same instinct as Morris Traversal, which borrowed the tree's null pointers to remember the way back, and it appears whenever a "build it in place" follow-up shows up. The general question to ask when an interviewer says "now do it in O(1) space" is: what am I storing in that auxiliary structure, and is that information already present, or already being written, somewhere in the data?
Populating Next Right Pointers
Connecting Siblings at Each Level
Key Mechanics
- Level-by-Level: Process nodes in each level from left to right using BFS.
- Queue Peek: Each node's next pointer points to the front of the queue (next sibling).
- Last Node: The rightmost node in each level points to NULL.
Real-World Applications
This pattern is essential for level-order linked lists, serialization of perfect binary trees, and multi-threaded tree traversals where each thread processes one level. It's also used in game development for connecting entities at the same depth in scene graphs.
Strategy
Focus on the recursive nature of trees: solve for subtrees and combine results at the root.
"Divide and Conquer: Subproblem → Recurrence → Result"