二叉树 10 (填充每个节点的下一个右侧节点指针 leetcode 116)

50 阅读3分钟

思想

二叉树的核心思想是分治和递归,特点是遍历方式。
解题方式常见两类思路:

  1. 遍历一遍二叉树寻找答案;
  2. 通过分治分解问题寻求答案;

遍历分为前中后序,本质上是遍历二叉树过程中处理每个节点的三个特殊时间点:

  1. 前序是在刚刚进入二叉树节点时执行;
  2. 后序是在将要离开二叉树节点时执行;
  3. 中序是左子树遍历完进入右子树前执行;
# 前序
     1 node
    /      \
 2 left   3 right
中左右
 
# 中序
     2 node
    /      \
 1 left    3 right
左中右
 
# 后序
     3 node
    /      \
 1 left    2 right     
左右中       

多叉树只有前后序列遍历,因为只有二叉树有唯一一次中间节点的遍历

题目的关键就是找到遍历过程中的位置,插入对应代码逻辑实现场景的目的。

实例

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

class TreeNode:
    def __init__(self, val=0, left=None, right=None, next=None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next

输入:
root: TreeNode,二叉树的根节点

输出:
root: TreeNode,完成二叉树每个节点的 next 指针填充,返回根节点

举例:
给定二叉树 [1,2,3,4,5,6,7]
翻转返回,[1,#,2,3,#,4,5,6,7,#]

    1                 1 -> None
   / \               / \
 2    3      =>     7 -> 2 -> None
/ \  / \           / \  / \
4 5  6  7         9->6->3->1 -> None

二叉树的数据存储可以使用链表,也可以使用数组,往往数组更容易表达,根节点从 index=1 处开始存储,浪费 index=0 的位置
left_child = 2 * parent
right_child = 2 * parent + 1 parent = child // 2

遍历解

这个题目有两个关键点:

  1. 识别各层,这个在之前的文章中有分析,不再赘述,重点是判断当前节点入队的高度和当前高度是否匹配,相等说明进入了新的一层,因为对左右子树入队的操作时都将层高加了 1;
  2. 每层最后的节点如何统一处理,设置为 None。这里一种常用的思路是占位,即当识别到进入新的一层时,在本层入队新的节点前,先插入一个 None 的节点,用作上层最后一个节点的 next 指向。

分治解

将问题还原成最基础的状态:如果想要指向右侧节点,一定需要含有两个节点进行递归。
两个节点有两种情况:
1.当前节点的左右子树节点;
2.两个同层相邻节点的左右子树相连节点。

所以递归的处理下一层这两种子节点的相邻关系,然后本层将收到的两个节点关联即可。

编码


from typing import Optional


class TreeNode:
    def __init__(self, val=0, left=None, right=None, next=None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next


def populating_next_right_pointers_in_each_node(root: Optional[TreeNode]) -> Optional[TreeNode]:
    # 初始化
    level_queue = []
    height = 0
    if root is not None:
        level_queue.insert(0, (height, root))
    # 遍历求解
    while level_queue:
        cur_height, cur_node = level_queue.pop()
        if cur_node is None:
            continue
        # 前序位置
        if cur_height == height:
            # 进入新一层,占位 None 节点,用作上一层尾部对象的 next 指向
            level_queue.insert(0, (height, None))
            height += 1
        if cur_node.left:
            level_queue.insert(0, (height, cur_node.left))
        if cur_node.right:
            level_queue.insert(0, (height, cur_node.right))
        # 后序位置,当前节点指向队列最后一个元素的节点
        cur_node.next = level_queue[-1][1]

    return root


def populating_next_right_pointers_in_each_node_recursive(root: Optional[TreeNode]) -> Optional[TreeNode]:
    def traverse(node1: TreeNode, node2: TreeNode):
        # base 条件,任意节点为空,无序处理
        if node1 is None or node2 is None:
            return
        # 前序位置,将传入两个节点串联
        node1.next = node2
        # 连接相同父节点的两个子节点
        traverse(node1.left, node1.right)
        traverse(node2.left, node2.right)
        # 连接跨越父节点的两个子节点
        traverse(node1.right, node2.left)

    # 边界保护
    if root is None:
        return None
    traverse(root.left, root.right)
    return root

相关

二叉树 0
二叉树 1
二叉树 2
二叉树 3
二叉树 4
二叉树 5
二叉树 6
二叉树 7
二叉树 8
二叉树 9