二叉树的理论基础
二叉树的种类:满二叉树(没有空隙)、完全二叉树(只有最下层右边可能为空)、二叉树、二叉搜索树(左子树小于根节点,右子树大于根节点)、平衡二叉搜素树(在搜索树的基础上,左右子树高度不大于1)、平衡树(左右子树高度不大于1)
存储方式:顺序存储、链式存储
遍历方式:深度优先遍历:前中后序、迭代遍历
广度优先遍历:层序遍历
定义方式:
class TreeNode{
int val;
TreeNode left;
TreeNode right;
TreeNode() {}
TreeNode(int val){
this.val=val;
}
TreeNode(int val,TreeNode left,TreeNode right){
this.val=val;
this.left=left;
this.right=right;
}
}
二叉树的遍历方式
深度优先遍历:
- 前中后序递归遍历:递归三部曲
- 前中后序迭代法:通过栈模拟递归
- 前中后序的统一迭代法
广度优先遍历
- 层序遍历:通过队列模拟
掌握以上各种方法的写法
二叉树的属性
二叉树:是否对称
- 递归:后序遍历,比较根节点的左右子树是否相互翻转
- 迭代:使用栈或队列得到节点值比较是否对称
二叉树:求最大深度
- 递归:后序,求根节点的最大高度就是最大深度,注意深度与高度的区别
- 迭代:层序遍历
二叉树:求最小深度
- 递归:后序
- 迭代:层序遍历
二叉树:求有多少个节点
- 递归:后序
- 迭代:层序遍历
二叉树:是否平衡
- 递归:后序,注意后序求高度,前序求深度
- 迭代:效率低
二叉树:找所有路径
- 递归:前序,涉及回溯处理根节点到叶子节点的所有路径
- 迭代:一个栈模拟递归,存取对应的遍历路径
二叉树:递归中如何隐藏着回溯
- 找所有路径中回溯如何体现?与上题相关
二叉树:求左叶子之和
- 递归:后序,注意判断左叶子的条件
- 迭代:层序遍历每次找最后一行最左边
二叉树:求左下角的值
- 递归:优先左孩子遍历,找到深度最大的左叶子节点
- 迭代:最后一行最左边
二叉树:求路径总和
- 递归:遍历顺序无关,递归函数如果有Boolean返回值是为了搜索一条边,没有返回值是搜索整棵树
- 迭代:栈里的元素不仅要记录节点指针,还要记录从头结点到该节点的路径和
二叉树的修改与构造
翻转二叉树
- 递归:前序,交换左右孩子
- 迭代:模拟前序遍历
构造二叉树
- 递归:前序,重点在于从前后序找分割点然后分割中序数组,再分割前后序数组,前序中序,中序后序可以唯一确定一个二叉树,前序与后序不行
- 迭代:复杂
构造最大的二叉树
- 递归:前序,分割点为数组最大值,分割左右区间构造
- 迭代:复杂
合并二叉树
- 递归:前序,同时操作两个树的节点
- 迭代:使用队列,类似层序遍历
求二叉搜索树的属性
二叉搜索树中的搜索
- 递归:注意二叉搜索树的特性,不用遍历完
- 迭代:简单
是不是二叉搜索树
- 递归:中序,比较左右孩子与根节点值
- 迭代:用栈模拟中序
求二叉搜索树的最小绝对值
- 递归:中序,得到递增的数组,注意最小值只可能出现在相邻节点差处
- 迭代:模拟中序
求二叉搜索树的众数
- 递归:用哈希表记录节点值出现频率,根据最大值即最大频率得到对应的键
二叉搜索树转换成累加树
- 递归:反中序,双指针(用一个变量记录前一个节点值)
- 迭代:模拟中序
二叉树公共祖先问题
二叉树的公共祖先问题
- 递归:后序,分别从左右子树中查找是否出现要查找的值,结合左右子树返回值考虑
- 迭代:不适合模拟递归
二叉搜索树的公共祖先问题
- 递归:遍历顺序无关,二叉搜索树根节点处于左右节点区间中,如果节点值出现在目标区间就是最近公共祖先
- 迭代:按序遍历
二叉搜索树的修改与构造
二叉搜索树中的插入操作
- 递归:遍历顺序无所谓,通过递归插入节点
- 迭代:按序遍历,需要记录插入父节点,才能左插入操作
二叉搜索树中的删除操作
- 递归:找到要删除的节点后,要搞清楚四种情况:孩子为空直接删、左孩子为空(右孩子顶上)、右孩子为空(左孩子顶上)、左右都不为空(将删除节点的左子树放到删除节点右子树的最左边)
- 迭代:复杂
修剪二叉搜索树
- 递归:前序,如果左孩子不满足就返回右孩子,右孩子不满足就返回左孩子
- 迭代:复杂
构造二叉搜索树
- 递归:前序,根据中间节点分割数组
- 迭代:复杂
总结
- 二叉树构造最麻烦的问题就是什么遍历顺序,无论是什么二叉树,涉及到构造一定是前序遍历,先构造中间节点。
- 求普通二叉树的属性,一般是后序。
- 求二叉搜索树的属性,一般是中序。