117. 填充每个节点的下一个右侧节点指针 II
题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii
题目
给定一个二叉树
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
进阶:
- 你只能使用常量级额外空间。
- 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
示例:
![2e951fa7ded7168f7a426ff2fc84340c.png](https://i-blog.csdnimg.cn/blog_migrate/9e457919383f1263a40138bfdd3452dc.png)
输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。
提示:
- 树中的节点数小于 6000
- -100 <= node.val <= 100
解题思路
思路:层序遍历
与这道题类似的还有下面的题目:
116. 填充每个节点的下一个右侧节点指针
现在先审题,题目给定一个二叉树,要求填充每个节点的 next 指针,让指针指向下一个右侧节点。若是找不到下一个右侧节点,将 next 指针设置为 NULL。(初始状态下,next 指针设置都为 NULL)。
这里我们结合示例来看,我们可以发现,可以使用层序遍历的思路。依据层序遍历,我们逐层去遍历二叉树,那么这样就可以依次填充节点的 next 指针。
具体的做法如下:
- 首先声明一个辅助队列,先将根节点放入队列中;
- 开始循环遍历,首先统计队列的大小,这样可以保证遍历的是同一层的节点;
- 由于每层节点从左到右依次入队,当节点出队时,当前节点的 next 指针则指向队列的头部节点(此时头部节点为当前节点的下一个右侧节点),同时记录出队节点的下一层节点。
- 这里注意逐层的最后个节点,由于无下一个右侧节点,可不做处理,因为所有节点 next 指针初始设置为 NULL。
- 循环直至队列为空。
代码的实现如下:
"""
# 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
from collections import deque
# 声明辅助队列,先将根节点入队
queue = deque()
queue.append(root)
# 遍历开始
while queue:
# 先统计队列的大小
size = len(queue)
# 开始逐层遍历
for i in range(size):
# 节点出队
node = queue.popleft()
# 记录当前节点的下一层节点
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
# 不是最后个节点时,
# 让当前节点的 next 指针指向队列的头部节点
if i != size-1:
node.next = queue[0]
return root
这道题中,还有个进阶的内容,规定只能使用常量级的额外空间。上面的方法中,使用了辅助队列,这里需要 O(n) 的空间开销。那么现在的思路就是如何在不使用辅助队列的情况下,实现填充 next 指针。
在这里,需要定义三个变量,用以填充 next 指针,分别如下:
- head:下一层的头结点;
- pre: 下一层的前驱节点;
- cur:当前层访问节点。
在这里,给定的是二叉树,我们可以通过上一层的节点访问下一层的节点。只要能让下一层最左侧的节点作为开始,我们就能够通过上一层的节点,一边访问一边填充节点的 next 指针。
这里简单说下具体的做法:
- 先处理根节点,令 head=root;
- 逐层开始访问填充前, cur 由 head 赋值(逐层存在节点时,head 始终指向头部)。同时填充前需重置 pre、head 为 None,这里表示下一层还未开始填充,未找到 pre、head。
- 开始填充时,需要维护更新 cur、pre 及 head;
- 定义
fill(node)
函数,用以维护更新 pre 和 head(其中传入参数 node 是 cur 的左右子节点):- 这里先对传入的节点进行判断,若节点为 None,直接返回,否则继续下面的步骤;
- 查找下层的前驱节点,当不存在时,令 pre、head 等于当前节点 node,确定下层的起始点;若存在,则表示下层正在填充,令 pre.next = node,然后移动 pre;
- 同样,cur 左右子节点都经过访问之后,要移动 cur。
(写上面一段内容时,感觉有点混乱,具体看下代码看是否能够有所帮助,或者可以尝试草稿上画图。)
具体代码如下:
"""
# 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':
def fill(node):
nonlocal pre, head
# 如果传入的节点不存在,返回 None
if not node:
return
# 如果 pre 为空,表示无前驱节点,此时将传入的节点赋值给 pre
# 同时赋值给 head,表示下一层的头结点
if not pre:
head = pre = node
# 若 pre 不为空,表示正在填充下一层
# 填充 next 指针,同时移动 pre
else:
pre.next = node
pre = pre.next
head = root
while head:
cur = head
# 先初始化 pre、head
pre = head = None
# 根据当前层,填充下一层
while cur:
fill(cur.left)
fill(cur.right)
# cur 左右子树进行访问判断之后,进行移动
cur = cur.next
return root
欢迎关注
公众号 【书所集录】
- END -