@laigary.com~/interview/coding/117-populating-next-….md$
$ cat ./coding/117-populating-next-right-pointers-in-each-node-ii.md
[Coding]·2023-01-29·6 min read

117. Populating Next Right Pointers in Each Node II

117. Populating Next Right Pointers in Each Node II

116 一樣要把每個節點的 next 指向右邊的同層節點,差別是這題的樹是任意形狀,不再保證是完美二元樹。

思路

用分層 BFS 的話,這題跟 116 的程式碼完全一樣,一行都不用改。

原因是分層 BFS 從來就不依賴樹的形狀 —— 它只問「佇列裡現在有幾個節點」,那幾個就是這一層的全部。不管樹缺了多少節點、左右多不平衡,同層節點在佇列裡依然是照左到右排好的,queue[0] 依然是右邊的鄰居。

所以 116 到 117 的「升級」,對佇列解法來說是免費的。這件事本身值得記住:題目條件變寬鬆了,但解法沒有用到那個條件,所以不受影響。

(真正被影響的是 116 那個 O(1) 空間的 follow-up 解 —— 它靠「左子節點的 next 一定是自己的右子節點」這種完美樹才有的結構保證,到了 117 就得改寫成「往右一路找到第一個存在的子節點」。)

分層的骨架見 102. 層序遍歷

解題方向

"""
# 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: 'Node') -> 'Node':
        if not root:
            return root
        queue = deque([root])

        while queue:
            size = len(queue)
            for i in range(size):
                node = queue.popleft()
                if i < size - 1:
                    node.next = queue[0]
                if node.left:
                    queue.append(node.left)
                if node.right: 
                    queue.append(node.right)

        return root

i < size - 1 確保每層最後一個節點的 next 維持預設的 None,不會誤串到下一層的第一個節點。

補充

這裡原本寫的是 queue.pop(0),那會直接噴 TypeError

TypeError: deque.pop() takes no arguments (1 given)

list.pop(0) 可以彈出最前面的元素,但 deque.pop() 不吃任何參數 —— 它固定從右邊彈出。要從左邊彈只能用 popleft()

順帶一提,就算把 queue 改成 listpop(0) 能跑,也不該這樣寫:list.pop(0)O(n)(後面所有元素都要往前搬),整個 BFS 會退化成 O(n2)。這是 BFS 最常見的效能地雷,102 那篇也提過。

同一個骨架的家族

題目在那一層裡做什麼
102. Level Order收集整層
199. Right Side View只取最後一個
515. Largest Value in Each Row取 max
1302. Deepest Leaves Sum求和,只留最後一層
103. Zigzag Level Order奇數層反轉
116 / 117next 串起同層節點

整理見 Tree 遍歷模板

複雜度

  • 時間 O(n) — 每個節點進出佇列各一次
  • 空間 O(w)w 是最寬那層的節點數,最壞 O(n)

其中 n 是節點數。題目的 follow-up 一樣要求 O(1) 額外空間,佇列版嚴格來說不符合,但判題會過。