LeetCode 二叉树专项填充每个节点的下一个右侧节点指针(116)

Posted TakingCoding4Granted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 二叉树专项填充每个节点的下一个右侧节点指针(116)相关的知识,希望对你有一定的参考价值。

1. 题目

给定一棵完美二叉树,即该二叉树的所有叶子结点都在同一层且每一个父结点都有两个子结点,二叉树的结点定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

请你填充每个 next 指针,使得该指针指向该结点右边第一个同层结点,如果当前结点右边无结点,请将 next 指针设为 null

初始状态下每一个 next 指针都是 null

1.1 示例

  • 示例 1 1 1

  • 输入: root = [1, 2, 3, 4, 5, 6, 7]

  • 输出: [1, #, 2, 3, #, 4, 5, 6, 7, #]

  • 说明: 给定如下图 A 所示的完美二叉树,你的解答应该给出如图 B 所示的结果。序列化的输出按层序遍历(即广度优先遍历)排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。

1.2 说明

1.3 限制

  • -1000 <= Node.val <= 1000
  • 二叉树的结点总数范围为 [ 0 ,   2 12 − 1 ] [0,\\textit{ }2^{12} - 1] [0, 2121]

1.4 进阶

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

2. 解法一(广度优先遍历)

2.1 分析

由示例很明显可以看出来每一层的结点之间按照广度优先遍历的顺序通过 next 指针进行了连接。

2.2 解答

from collections import deque


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 root is None:
            return
        visiting = deque([root])
        while len(visiting) > 0:
            if len(visiting) == 1:
                node = visiting.popleft()
                if node.left:
                    visiting.append(node.left)
                if node.right:
                    visiting.append(node.right)
                continue
            for i in range(len(visiting) - 1):
                visiting[i].next = visiting[i + 1]
            for i in range(len(visiting)):
                node = visiting.popleft()
                if node.left:
                    visiting.append(node.left)
                if node.right:
                    visiting.append(node.right)
        return root

实际上,上述解答的核心代码没有能够包含双端队列 visiting 中仅有根结点的情况,下面是对此的优化:

from collections import deque
from typing import Optional


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) -> Optional[Node]:
        if root is None:
            return
        visiting = deque([root])
        while len(visiting) > 0:
            node = visiting[0]
            for i in range(1, len(visiting)):
                node.next = visiting[i]
                node = visiting[i]
            for i in range(len(visiting)):
                node = visiting.popleft()
                if node.left:
                    visiting.append(node.left)
                if node.right:
                    visiting.append(node.right)
        return root

更进一步地,即使是上述实现,也需要使用两次的循环遍历,一次是对每一层结点的 next 指针进行填充,另一次是将当前层结点的子结点加入双端队列。下面给出继续优化的代码:

from collections import deque
from typing import Optional


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) -> Optional[Node]:
        if not root:
            return root
        visiting = deque([root])
        while visiting:
            size = len(visiting)
            for i in range(size):
                node = visiting.popleft()
                if i < size - 1:
                    node.next = visiting[0]
                if node.left:
                    visiting.append(node.left)
                if node.right:
                    visiting.append(node.right)
        return root
  • 执行用时: 52 ms , 在所有 Python3 提交中击败了 91.57% 的用户;
  • 内存消耗: 16.5 MB , 在所有 Python3 提交中击败了 86.35% 的用户。

实际上,从上述的优化代码可以得出一个编程的原则:即在使用 for 循环时,尽量避免将循环结束的条件变量化,例如这里使用 size 而非 len(visiting) 来指示 for 循环的结束,否则当 visiting 中的元素变化时, for 循环结束的条件会改变,进而可能导致意料之外的情况。

2.3 复杂度

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)。这是一棵完美二叉树,它的最后一个层级包含的节点数量为总结点个数的一半,此时占用的辅助双端队列容量最大。

以上是关于LeetCode 二叉树专项填充每个节点的下一个右侧节点指针(116)的主要内容,如果未能解决你的问题,请参考以下文章

[leetcode-117]填充每个节点的下一个右侧节点指针 II

11道精选经典LeetCode例题让你彻底搞懂二叉树的广度优先遍历

LeetCode 116. 填充每个节点的下一个右侧节点指针

[LeetCode] 116. 填充每个节点的下一个右侧节点指针

Leetcode 116. 填充每个节点的下一个右侧节点指针

Leetcode No.116 填充每个节点的下一个右侧节点指针