You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has the following definition:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.
Initially, all next pointers are set to NULL.
Example 1:
Input: root = [1,2,3,4,5,6,7]
Output: [1,#,2,3,#,4,5,6,7,#]Explanation: Given the above perfect binary tree (Figure A), your function should populate each next pointer to point to its next right node, just like in Figure B. The serialized output is in level order as connected by the next pointers, with '#' signifying the end of each level.
Example 2:
Input: root = [1]
Output: [1,#]Constraints:
0 <= number of nodes in the tree <= 4095-1000 <= Node.val <= 1000Follow-up:
Before attempting this problem, you should be comfortable with:
Level-order traversal naturally groups nodes by their depth, which is exactly what we need to connect nodes on the same level. As we process each level, the next node in the queue is the right neighbor of the current node.
By tracking the size of each level, we can connect all nodes within a level while avoiding connecting the last node of one level to the first node of the next.
root is null, return null.root.next pointer to the front of the queue.left and right children if they exist.root."""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if not root:
return None
q = deque([root])
while q:
levelSize = len(q)
while levelSize:
node = q.popleft()
if levelSize > 1:
node.next = q[0]
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
levelSize -= 1
return rootUsing dfs, we can traverse the tree and track the rightmost node seen at each depth. When we visit a node, we connect the previous rightmost node at that depth to the current node, then update the rightmost reference.
A hash map stores the most recently visited node at each depth, allowing us to build the next pointers as we traverse left to right.
dfs function that takes a node and its depth:null, return.next pointer to the current node.left child, then the right child (both with depth + 1).dfs starting from the root at depth 0.root."""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
mp = {}
def dfs(node, depth):
if not node:
return
if depth not in mp:
mp[depth] = node
else:
mp[depth].next = node
mp[depth] = node
dfs(node.left, depth + 1)
dfs(node.right, depth + 1)
dfs(root, 0)
return rootIn a perfect binary tree, every node has either zero or two children, and all leaves are at the same level. This structure lets us establish next pointers without extra space for tracking.
For any node with children, its left child's next is always its right child. And if the node has a next pointer, its right child's next is the left child of the node's next neighbor. This recursive pattern connects the entire tree.
root is null, return it.root has a left child:left child's next to the right child.root has a next pointer, set the right child's next to the next node's left child.left subtree.right subtree.root."""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
if not root:
return root
if root.left:
root.left.next = root.right
if root.next:
root.right.next = root.next.left
self.connect(root.left)
self.connect(root.right)
return rootInstead of using a queue, we can leverage the next pointers we've already established to traverse each level. We process the tree level by level, using the current level's next pointers to iterate horizontally while setting up the connections for the next level.
Two pointers track our position: one for the current node being processed, and one for the leftmost node of the next level (so we know where to start the next iteration).
cur to the root and nxt to the root's left child (the start of the next level).cur and nxt are not null:cur.left.next to cur.right.cur.next exists, connect cur.right.next to cur.next.left.cur to cur.next.cur becomes null, move to the next level: set cur to nxt and nxt to cur.left.root."""
# Definition for a Node.
class Node:
def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
self.val = val
self.left = left
self.right = right
self.next = next
"""
class Solution:
def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
cur, nxt = root, root.left if root else None
while cur and nxt:
cur.left.next = cur.right
if cur.next:
cur.right.next = cur.next.left
cur = cur.next
if not cur:
cur = nxt
nxt = cur.left
return rootWhen iterating through nodes, it is easy to accidentally set the last node of one level to point to the first node of the next level. Always track the level size or use a marker to know when a level ends, ensuring the rightmost node's next stays null.
For a node's right child, its next should be the left child of the node's next neighbor (if it exists). Forgetting this connection leaves gaps in the horizontal links, breaking the chain between subtrees rooted at different parents.
The O(1) space solution leveraging the perfect binary tree structure (every node has 0 or 2 children, all leaves at same level) does not generalize. Applying it to arbitrary binary trees produces incorrect results because the assumptions about child existence fail.