思想
二叉树的核心思想是分治和递归,特点是遍历方式。
解题方式常见两类思路:
- 遍历一遍二叉树寻找答案;
- 通过分治分解问题寻求答案;
遍历分为前中后序,本质上是遍历二叉树过程中处理每个节点的三个特殊时间点:
- 前序是在刚刚进入二叉树节点时执行;
- 后序是在将要离开二叉树节点时执行;
- 中序是左子树遍历完进入右子树前执行;
# 前序
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;
- 每层最后的节点如何统一处理,设置为 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