Leetcode笔记-二叉树

127 阅读4分钟

前/中/后序遍历

前/中/后序遍历的区别,本质上是根节点在左右子节点之前、之间、还是之后遍历。

遍历本质上有两种方式:

  • 深度优先dfs:
    • 深入到底+回溯,使用栈(递归/显式栈迭代)实现。
    • 递归实现时,通常在遍历函数内部,定义一个dfs函数,其功能为在每个节点,对当前节点做操作,并继续访问子节点。
    • 显式栈实现时:
      • 前序、后序:【写出二叉树的非递归遍历很难么?这次让你不再害怕非递归!|二叉树的非递归遍历 | 二叉树的遍历迭代法 | 前序与中序-哔哩哔哩】 b23.tv/IpL50L9
      • 中序:【写出二叉树的非递归遍历很难么?这次再带你写出中序遍历的迭代法!|二叉树的非递归遍历 | 二叉树的遍历迭代法-哔哩哔哩】 b23.tv/i6Nnl8F
  • 广度优先bfs:逐层搜索,使用队列实现。

倒过来看,如何用遍历结果构造二叉树呢?

  • 前序与中序/后序与中序:基于前序/后序,定位根节点。基于根节点,分割中序,定位左右子树。递归求解即可。
  • 注意,不能前序与后序,因为无法定位左右子树。

路径总和

题目简单,二叉树一般先考虑递归做法。

注意细节:空树返回False - 影响递归终止条件。

路径总和II

找所有路径:回溯。

  • 如果解决一个问题有多个步骤,每一个步骤有多种方法,题目又要我们找出所有的方法,可以使用回溯算法
  • 回溯算法是在一棵树上的 深度优先遍历(因为要找所有的解,所以需要遍历)
  • 注意,return之前,如果已经加入新节点,一定要pop(即便是边界条件处理)。

路径总和III

与【路径总和II】相比:

  • 区别:只计算路径数量;允许不经过根节点。
  • 解法:再套一层遍历(dfs、bfs皆可),遍历到每个节点时,计算以该节点为根节点的【路径总和II】数量。
  • 核心思路:二叉树的最重要思路,递归解决问题,将问题拆分为子问题。

二叉树的最近公共祖先

首先尝试递归,需要先拆解子问题:

  • 找最近公共祖先,要么在根节点找到,否则就分别往左右子树去找。
  • 找的时候有两种可能性:找到公共祖先;找到节点p/q。
  • 注意尽管题目假设树不为空,仍要考虑root为空的边界条件,因为递归会遇到。

二叉树的右视图

显然BFS:

  • 每层节点,按从右到左的顺序进入队列。
  • 每次只取队列第一个元素即可。

二叉树展开为链表

思路一:前序遍历,存储所有节点到列表,重新连接。

思路二:按题目要求优化空间复杂度,原地修改。递归,注意细节即可:

  • 修改交互关系前临时存储节点。
  • 递归完成左子树之后,需要步进到最末端,再和右子树相连。

将有序数组转换为二叉搜索树

关键思路:

  • 二叉搜索树对应的有序数组,即为二叉搜索树的中序遍历。
  • 单凭中序遍历,无法确定根节点位置。题目要求平衡二叉树,即取数组中间元素,作为根节点。
  • 左右子树递归即可。
  • 边界情况:空数组。

删除二叉搜索树中的节点

删除在单个节点的操作,分为三种情况:

  • 无左右子树,直接删除。
  • 有单侧子树,替换。
  • 有左右子树,选取左子树的最大节点/右子树的最小节点替换。

思路一:二叉树永远优先考虑递归。

  • 根节点不是目标,按值大小,选择递归左/右子树即可。
  • 根节点是目标,则需考虑上述三种情况。第三种情况最复杂,需要先找到替换的节点,将替换节点从原子树中删去,再进行替换。此处注意先后顺序,不可先替换,否则子树被修改。

思路二:迭代,逻辑细节非常复杂。