python 指针_LeetCode 117. 填充每个节点的下一个右侧节点指针 II | Python

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
示例 1
输入: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 -
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值