二叉树 TODO

知识点

二叉树遍历

二叉树的遍历方式,以访问根节点的顺序不同,有3种分别为:

  • 前序遍历根节点 --> 左子树 --> 右子树

  • 中序遍历:左子树 --> 根节点 --> 右子树

  • 后序遍历:左子树 --> 右子树 --> 根节点

左子树都是优先右子树

在解释具体的遍历方式前,我们先定义树的结构

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

前序递归

def pre_order_traversal(root: TreeNode):
    if root == null:
        return

    # 根节点 --> 左子树  --> 右子树
    print(root.val)
    preorder_traversal(root.left)
    preorder_traversal(root.right)

前序非递归

中序非递归

后序非递归

核心:根节点必须在右节点弹出之后,再弹出

DFS 深度搜索-从上到下

DFS 深度搜索-从下向上(分治法)

DFS 两种实现方式区别

  • 从上到下, 一般将最终结果通过指针参数传入

  • 分治法, 一般递归返回结果最后合并

BFS 层次遍历

分治法应用

先分别处理局部,再合并结果

适用场景

  • 快速排序

  • 归并排序

  • 二叉树相关问题

分治法模板

  • 递归返回条件

  • 分段处理

  • 合并结果

典型示例

归并排序

递归需要返回结果用于合并

快速排序

快排由于是原地交换所以没有合并过程 传入的索引是存在的索引(如:0、length-1 等),越界可能导致崩溃 核心进行partition, 分成左右两部分

常见题目示例

给定一个二叉树,找出其最大深度。

思路

  • 分治法

    • 左边深度

    • 右边深度

    • 左右深度最大值 + 1

给定一个二叉树,判断它是否是高度平衡的二叉树。

高度平衡的二叉树: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

思路

  • 分治法, 需要同时满足以下条件

    • 左边高度平衡

    • 右边高度平衡

    • 左右两边高度 <= 1

  • 需要返回是否平衡及高度(二义性:一个变量有两种含义)

    • -1: 不平衡

    • > 0: 树高度

注意

一般工程中, 不建议用一个变量表示两种含义

给定一个非空二叉树,返回其最大路径和。

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中至多出现一次 。该路径至少包含一个节点,且不一定经过根节点。

路径和 是路径中各节点值的总和。

思路 分治法,分为三种情况:

  1. 左子树最大路径和最大

  2. 右子树最大路径和最大

  3. 左右子树最大贡献加根节点最大

需要保存两个变量

  • 最大路径和:max_path

    • 不包含, max(左、右最大路径)

    • 包含value, max(左+右节点最大贡献, 左节点最大贡献, 右节点最大贡献, 0) + value

  • 当前节点对父节点最大贡献: max_gain

    • 不包含, 0

    • 包含节点, max(左、右节点最大贡献, 0) + value

  • 当节点为空时

    • 最大路径应该, 很小的负数, 不然上层节点值为负时, 最大路径可能选择错误

然后比较这个两个变量选择最大值即可

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

最近公共祖先, 对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足

  • x 是 p、q 的祖先

  • 且 x 的深度尽可能大

注, 一个节点也可以是它自己的祖先

思路

分治法

  1. root 为空, 此时公共祖先不存在

  2. root 不为空, p, q 一定在为子树

    • p 或 q == root, 则其最近公共祖先为必须包含root, 有可能p或q不在root下

    • p, q均不等于root

      • 分治

        • common_left = divide(root.left, p, q)

        • common_right = divide(root.right, p, q)

      • 合并

        • common_left, common_right 都不为空, 返回root

          • 一个分支==p

          • 另一个分支==q

        • common_left, common_right 有一个不为空, 则说明p, q在其内部, 直接作为最终结果

  3. 有左子树的公共祖先或者有右子树的公共祖先,就返回子树的祖先,否则返回根节点

BFS 层次应用

binary-tree-level-order-traversal

binary-tree-level-order-traversalarrow-up-right

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)

思路:用一个队列记录一层的元素,然后扫描这一层元素添加下一层元素到队列(一个数进去出来一次,所以复杂度 O(logN))

binary-tree-level-order-traversal-ii

binary-tree-level-order-traversal-iiarrow-up-right

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

思路:在层级遍历的基础上,翻转一下结果即可

binary-tree-zigzag-level-order-traversal

binary-tree-zigzag-level-order-traversalarrow-up-right

给定一个二叉树,返回其节点值的锯齿形层次遍历。Z 字形遍历

二叉搜索树应用

validate-binary-search-tree

validate-binary-search-treearrow-up-right

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

思路 1:中序遍历,检查结果列表是否已经有序

思路 2:分治法,判断左 MAX < 根 < 右 MIN

insert-into-a-binary-search-tree

insert-into-a-binary-search-treearrow-up-right

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。

思路:找到最后一个叶子节点满足插入条件即可

总结

  • 掌握二叉树递归与非递归遍历

  • 理解 DFS 前序遍历与分治法

  • 理解 BFS 层次遍历

练习

Last updated