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 说明
- 来源: 力扣(LeetCode)
- 链接: https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node
1.3 限制
-1000 <= Node.val <= 1000
;- 二叉树的结点总数范围为 [ 0 , 2 12 − 1 ] [0,\\textit{ }2^{12} - 1] [0, 212−1] 。
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. 填充每个节点的下一个右侧节点指针