花了大办个月学习了卡尔老师代码随想录中的二叉树章节,使我对二叉树有了更加深刻的理解。下面就对二叉树章节做一个总结。
二叉树的基础知识
-
二叉树种类(满二叉树、完全二叉树、平衡二叉树、搜索二叉树等)
-
二叉树深度和高度
- 深度:该节点到根节点的最长路径距离,规定根节点深度为1(一般用先序遍历来求深度)
- 高度:该节点到叶子节点的最长路径,规定叶节点高度为1(一般用后序遍历来求高度)
-
存储方式(顺序存储、链式存储(用的最多)、领接表)
-
遍历方式(递归、迭代)
二叉树遍历方式
- 前中序递归(递归三部曲)
- 前中序迭代(栈模拟)
- 层次遍历(辅助队列)
- 102,103层次遍历I,II
- 199右视图
- 637层平均值
- N叉树层次遍历
- ....
二叉树的属性
- 是否对称、相同
- 递归:后序,比较的是根节点的左子树与右子树是不是相互翻转
- 迭代:使用队列/栈将两个节点顺序放入容器中进行比较
- 最大、最小深度
- 后序,求根节点最大高度就是最大深度,通过递归函数的返回值做计算树的高度
- 注意最小深度定义
- 迭代:层序遍历
- 求有多少个节点
- 递归:后序,通过递归函数的返回值计算节点数量
- 层序遍历
- 是否平衡
- 后序,注意后序求高度和前序求深度,递归过程判断高度差
- 迭代:效率很低,不推荐
- 找所有路径(递归中如何隐藏着回溯)
- 前序:方便让父节点指向子节点,涉及回溯处理根节点到叶子的所有路径
- 迭代:一个栈模拟递归,一个栈来存放对应的遍历路径
- 求左叶子之和
- 后序:必须三层约束条件,才能判断是否左叶子。
- 迭代:直接模拟后序遍历
- 求左下角的值
- 递归:遍历顺序无所谓,优先左孩子搜索,同时找深度最大的叶子节点。
- 层序遍历找最后一行最左边
- 求路径总和
- 顺序无所谓,递归函数返回值为bool类型是为了搜索一条边,没有返回值是搜索整棵树。
- 迭代:栈里元素不仅要记录节点指针,还要记录从头结点到该结点的路径数值总和
注意: 在合并二叉树、反转二叉树、是否对称中使用的迭代遍历不是真正的层次遍历,类似于层次遍历,使用辅助队列和栈都可以,注意同时进两颗树的节点,同时出两颗树的节点 注意:
二叉树的修改与改造
- 翻转二叉树
- 递归:前序,交换左右孩子
- 迭代:直接模拟前序遍历
- 构造二叉树
- 递归:前序,重点在于找分割点,分左右去见构造
- 构造最大的二叉树
- 前序:分割点为数组最大值,分左右去见构造
- 合并两个二叉树
- 递归:前序,同时操作两个树的节点,注意合并的规则
- 迭代:使用队列,类似层序遍历
注意: 区间确定性,整个算法过程中区间开闭都用一种,不然很容易混乱
求二叉搜索树的属性
- 二叉搜索树中的搜索
- 递归:二叉搜索树的递归是有方向性的【中序遍历】
- 迭代:因为有方向,所以迭代很简单
- 是不是二叉搜索树
- 递归:中序,相当于判断一个序列是不是递增的
- 迭代:模拟中序,逻辑相同
- 求二叉搜索树的最小结对差
- 递归:中序,双指针操作
- 迭代:模拟中序,逻辑相同
- 求二叉搜索树的众数
- 递归:中序,清空结果集的技巧,遍历一遍可求众数集合
- 二叉搜索树转成累加树
- 递归:中序,双指针操作累加
- 迭代:模拟中序,逻辑相同
二叉树公共祖先问题
- 二叉树的公共祖先问题
- 递归:后序,回溯,找到左子树出现目标值,右子树节点目标值的节点
- 迭代:不适合模拟回溯
- 二叉搜索树的公共祖先问题
- 递归:顺序无所谓,如果节点的数值在目标区间就是最近公共祖先
- 有序遍历
二叉搜树的修改与改造
- 二叉搜索树中的插入操作
- 递归:顺序无所谓,通过递归函数返回值添加节点
- 迭代:按序遍历,需要记录插入父节点,这样才能做插入操作
- 二叉搜索树中的删除操作
- 递归:前序,想清楚删除非叶子节点的情况
- 迭代:有序遍历,较复杂
- 修剪二叉搜索树
- 前序,通过递归函数返回值删除节点
- 迭代:有序遍历,较复杂
- 构造二叉搜索树
- 递归:前序,数组中间节点分割
- 迭代:较复杂,通过三个队列来模拟
最后总结
在二叉树题目选择什么遍历顺序是不少同学头痛的事情,卡哥给我们的分类如下:
- 涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点
- 求普通二叉树的顺序,一般是后序,一般要通过递归函数的返回值做计算
- 求二叉搜索树的顺序,一定是中序了 注意在普通二叉树的属性中,我们用的一般为后序,例如单纯求深度就用前序,二叉树:找所有路径也用了前序,这是为了方便让父节点指向子节点。 所以求普通二叉树的属性还是要具体问题具体分析。
二叉树专题汇聚为一张图:
本篇参考于代码随想录,侵删