参考:
代码随想录:二叉树总结篇
0、二叉树的理论基础
二叉树的种类、存储方式、遍历方式、定义方式
0.1 二叉树的种类
- 满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上
深度为k,有2^k-1个节点的二叉树 - 完全二叉树:除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。
若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。 - 二叉搜索树
有序树;
若左子树不空,左子树上所有结点的值均小于它的根结点的值;
若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
左、右子树也分别为二叉排序树; - 平衡二叉搜索树(AVL(Adelson-Velsky and Landis)树)
空树或它的左右两个子树的高度差的绝对值不超过1,
并且左右两个子树都是一棵平衡二叉树。
0.2 递归三部曲
- 确定递归函数的参数和返回值
- 确定终止条件
// 空节点,返回0 if (node == NULL) return 0; // 插入节点 if (root == NULL) { TreeNode* node = new TreeNode(val); return node; } //或者 // 找到叶子节点,就开始结束的处理逻辑 if (cur->left == NULL && cur->right == NULL) { 终止处理逻辑 }
- 确定单层递归的逻辑:调用递归的地方
0.3 递归函数什么时候要有返回值
- 搜索 整棵二叉树 且 不用处理递归返回值,递归函数就不要返回值。
113.路径总和ii - 搜索 整棵二叉树 且 需要处理递归返回值,递归函数就需要返回值。
236. 二叉树的最近公共祖先 - 搜索 其中一条符合条件的路径,那么递归 一定需要返回值
112. 路径总和
98.验证二叉搜索树
0.4 遍历顺序
- 涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。
- 求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。
一般为后序,例如单纯求深度就用前序,
二叉树:找所有路径 也用了前序,这是为了方便让父节点指向子节点。 - 求二叉搜索树的属性,一定是中序了,要不白瞎了有序性了。
0.5 深度 & 高度
联系:根节点的高度就是二叉树的最大深度
- 深度:从 根节点 到 该节点 的 最长简单路径边 的条数。
根节点是1,依次递增。 用 前序遍历(中左右) - 高度:从 该节点 到 叶子节点 的 最长简单路径边 的条数。
根节点最高,依次递减。 用 后序遍历(左右中)
0.6 在递归中如何记录前一个节点的指针
class Solution {
private:
int result = INT_MAX;
TreeNode* pre = NULL;
void traversal(TreeNode* cur) {
if (cur == NULL) return;
traversal(cur->left); // 左
if (pre != NULL){
// 中
result = min(result, cur->val - pre->val);
}
pre = cur; // 记录前一个
traversal(cur->right); // 右
}
public:
int getMinimumDifference(TreeNode* root) {
traversal(root);
return result;
}
};
1、二叉树的遍历方式
1.1 深度优先遍历
- 二叉树:前中后序递归法 :递归三部曲初次亮相
- 二叉树:前中后序迭代法(一):通过栈模拟递归
- 二叉树:前中后序迭代法(二)统一风格
1.2 广度优先遍历
- 二叉树的层序遍历:通过队列模拟
2、求二叉树的属性
2.1 二叉树:是否对称
- 递归:后序(左右中),比较的是根节点的左子树与右子树是不是相互翻转
- 迭代:使用队列/栈将两个节点顺序放入容器中进行比较
2.2 二叉树:求最大深度
- 递归:后序(左右中),求根节点最大高度就是最大深度,通过递归函数的返回值做计算树的高度
- 迭代:层序遍历
2.3 二叉树:求最小深度
- 递归:后序(左右中),求根节点最小高度就是最小深度,注意最小深度的定义
- 迭代:层序遍历
2.4 二叉树:求有多少个节点
- 递归:后序(左右中),通过递归函数的返回值计算节点数量
- 迭代:层序遍历
2.5 二叉树:是否平衡
- 递归:后序(左右中),注意后序求高度和前序求深度,递归过程判断高度差
- 迭代:效率很低,不推荐
2.6 二叉树:找所有路径
- 递归:前序(中左右),方便让父节点指向子节点,涉及回溯处理根节点到叶子的所有路径
- 迭代:一个栈模拟递归,一个栈来存放对应的遍历路径
2.7 二叉树:递归中如何隐藏着回溯
- 详解 二叉树:找所有路径 中递归如何隐藏着回溯
2.8 二叉树:求左叶子之和
- 递归:后序(左右中),必须三层约束条件,才能判断是否是左叶子。
- 迭代:直接模拟后序遍历
2.9 二叉树:求左下角的值
- 递归:顺序无所谓,优先左孩子搜索,同时找深度最大的叶子节点。
- 迭代:层序遍历找最后一行最左边
2.10 二叉树:求路径总和
- 递归:顺序无所谓,递归函数返回值为bool类型是为了搜索一条边,没有返回值是搜索整棵树。
- 迭代:栈里元素不仅要记录节点指针,还要记录从头结点到该节点的路径数值总和
3、二叉树的修改与构造
3.1 翻转二叉树
- 递归:前序(中左右),交换左右孩子
- 迭代:直接模拟前序遍历
3.2 构造二叉树
- 递归:前序(中左右),重点在于找分割点,分左右区间构造
- 迭代:比较复杂,意义不大
3.3 构造最大的二叉树
- 递归:前序(中左右),分割点为数组最大值,分左右区间构造
- 迭代:比较复杂,意义不大
3.4 合并两个二叉树
- 递归:前序(中左右),同时操作两个树的节点,注意合并的规则
- 迭代:使用队列,类似层序遍历
4、求二叉搜索树的属性
4.1 二叉搜索树中的搜索
- 递归:二叉搜索树的递归是有方向的
- 迭代:因为有方向,所以迭代法很简单
4.2 是不是二叉搜索树
- 递归:中序(左中右),相当于变成了判断一个序列是不是递增的
- 迭代:模拟中序,逻辑相同
4.3 求二叉搜索树的最小绝对差
- 递归:中序(左中右),双指针操作
- 迭代:模拟中序,逻辑相同
4.4 求二叉搜索树的众数
- 递归:中序(左中右),清空结果集的技巧,遍历一遍便可求众数集合
- 二叉搜索树转成累加树
- 递归:中序(左中右),双指针操作累加
- 迭代:模拟中序,逻辑相同
5、二叉树公共祖先问题
5.1 二叉树的公共祖先问题
- 递归:后序(左右中),回溯,找到左子树出现目标值,右子树节点目标值的节点。
- 迭代:不适合模拟回溯
5.2 二叉搜索树的公共祖先问题
- 递归:顺序无所谓,如果节点的数值在目标区间就是最近公共祖先
- 迭代:按序遍历
6、二叉搜索树的修改与构造
6.1 二叉搜索树中的插入操作
- 递归:顺序无所谓,通过递归函数返回值添加节点
- 迭代:按序遍历,需要记录插入父节点,这样才能做插入操作
6.2 二叉搜索树中的删除操作
- 递归:前序(中左右),想清楚删除非叶子节点的情况
- 迭代:有序遍历,较复杂
6.3 修剪二叉搜索树
- 递归:前序(中左右),通过递归函数返回值删除节点
- 迭代:有序遍历,较复杂
6.4 构造二叉搜索树
- 递归:前序(中左右),数组中间节点分割
- 迭代:较复杂,通过三个队列来模拟