2Brute2Best
πŸ”₯0

Populating Next Right Pointers in Each Node

MediumΒ·LeetCode #116Basic Anchor
LeetCode
30-Second Mental ModelTree next pointers: level walk with the level above as a linked list
Complexity:O(N)β†’O(N)
Key Insight

Use the next pointers of the row above as the queue. Start at the root. For each row, walk it with next and link the children of each node. Between two neighbouring parents, add one cross link from the right child to the next parent's left child. Then move down to the leftmost node of the row below.

Core Validity Invariant

Children of neighbouring nodes are neighbours too, so walking one row with next links the whole row below it.

01

Problem

You are given a perfect binary tree: every parent has exactly two children, and all leaves are on the same level. Each node has a next pointer, and all of them start as null.

Set every next pointer to the node on its right, on the same level. If there is no node on the right, set it to null. Return the root.

Input

The root of a perfect binary tree (it can be empty). Each node has val, left, right and next.

Output

The same root, with every next pointer filled in.

Example 1
Input: root = [1, 2, 3, 4, 5, 6, 7]
Output: [1, #, 2, 3, #, 4, 5, 6, 7, #]

The output lists the nodes level by level, following the next pointers. # marks the end of each level. Node 5 points to node 6, even though their parents (2 and 3) are different.

Example 2
Input: root = []
Output: []

An empty tree has no nodes, so there is nothing to set.

Constraints
  • β€’ The number of nodes is in the range [0, 2^12 βˆ’ 1]
  • β€’ βˆ’1000 ≀ Node.val ≀ 1000
  • β€’ Follow-up: can you use only constant extra space?
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck β€” each hint is counted.

  1. 1.

    What is a next pointer here? Which nodes are on the same level?

  2. 2.

    Can a node's next be a node with a different parent? Find one example in the tree above.

  3. 3.

    The tree is perfect. What does that tell me about how many nodes each level has?

  4. 4.

    A queue of one level gives me the nodes in order. How much extra space does it take, and can I avoid it?

  5. 5.

    Once a level is linked by next, can I use those links to walk that level and link the one below?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceBFS with a queue; link each popped node to the front of the queue unless it is the last of its roundO(N)O(W)
OptimalStart at the leftmost node; for each node on a row, link its children, plus one cross link to the next parent's left childO(N)O(1)
09

Why does this belong to this pattern?

Clues in the problem statement
  • β€œconnect each node to its right neighbour”Each row is a list of nodes. The row above can supply that list through its next pointers.
  • β€œperfect binary tree”Every row is full, so each node above the leaves has exactly two children. That lets the walk use cur->left without a null check.
  • β€œconstant extra space follow-up”No queue. The next pointers we are setting become the queue.
Tree row as a linked list+walk one row with next+link the row below+move down through leftmost→Tree next pointers: level walk with the level above as a linked list

This is level order with the queue replaced by the next pointers. A queue for one row is just the row's nodes in order. Once the row above is linked by next, that row is the list, so no queue is needed. The new part is the cross-parent link (right child β†’ next parent's left child). Populating Next Right Pointers II (LC 117) uses the same walk, but its tree has gaps, so the row below cannot be found from the leftmost node alone.

10

Don't make this mistake

Forgetting the cross-parent link (5 β†’ 6)
βœ— for (Node* cur = leftmost; cur; cur = cur->next) cur->left->next = cur->right;
βœ“ for (Node* cur = leftmost; cur; cur = cur->next) { cur->left->next = cur->right; if (cur->next) cur->right->next = cur->next->left; }

Node 5 (child of 2) and node 6 (child of 3) have different parents, so the first line never links them. Without the second line, 5's next stays null. The output becomes [1, #, 2, 3, #, 4, 5, #, 6, 7, #], and 5 should point to 6.

Pattern confusion: the queue's front is not always a neighbour
βœ— Node* cur = q.front(); q.pop(); cur->next = q.empty() ? nullptr : q.front();
βœ“ Node* cur = q.front(); q.pop(); if (i + 1 < size) cur->next = q.front(); // i + 1 < size: same round

When a round ends, the front of the queue is the first node of the next row, not a neighbour. On [1, 2, 3, 4, 5, 6, 7], node 3 is the last node of row 1, but the queue already holds 4, so 3 would point to 4. It should be null.

Walking one row too far
βœ— for (Node* leftmost = root; leftmost; leftmost = leftmost->left) { ... }
βœ“ while (leftmost && leftmost->left) { ... leftmost = leftmost->left; }

On the bottom row the leaves have no children, so cur->left is null. The line cur->left->next = cur->right then dereferences a null pointer and crashes. The loop must stop when the row below does not exist.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Tree BFS translate into production architectures and low-level memory mechanics.