1. 二叉树概述
1)定义:二叉树是每个节点最多有两个子树的树结构
2)种类
- 空二叉树:根节点为null
- 满二叉树 (Full):国内定义为,一个二叉树的每层的节点都达到最大值
- 完全二叉树 (Complete):除最后一层外,其他各层节点数都达到最大个数,并且最后一层的所有节点都集中在左边
- 平衡二叉树 (Balanced):左右子树的高度之差 <= 1,任意子树也必须是一颗平衡二叉树
2. 分治法
1)思路:将大规模问题拆分为若干个同类型子问题去处理的算法思想
2)应用
- 二叉树:整棵树的左子树和右子树还是二叉树。
- 数组:一个大数组可以拆分为若干不相交的子数组,例如快排、归排。
3)二叉树DFS的三种方式:前序,中序,后序
3. 相关例题
核心思路:遇到二叉树的问题,就想想整棵树在该问题上的结果,和左右子树在该问题上的结果之间有什么联系。
3.1 二叉树上求值
LintCode 596 · 最小子树
思路:
- 树的和 = 根节点值 + 左子树和 + 右子树和
- 将最小子树和,以及对应的子树根节点设置为全局变量。
- 利用分治的思想,递归求得各子树的和,并不断更新minSum和minRoot
LeetCode 236. 二叉树的最近公共祖先
要点:输入的两个点p和q是node object;两个节点一定都在树中存在
思路:用递归的方法分情况讨论
- 假设当前节点为null,则返回null
- 假设当前节点为p或q,则返回当前节点root
- 假设当前节点的左子树和右子树的返回值均不为null,则当前节点是最近公共祖先,返回root
- 左子树或右子树的返回值不为null时,返回那个不为null的节点
- 若以上情况都不满足,则返回null
LintCode 578 · 最近公共祖先 III
要点:两个指定的节点不一定存在于树中
思路:相比于上一道题,需要两个额外信息,即a_exist和b_exist来表示是否找到点A和B。
- 当前树是否有A = 左子树有A 或 右子树有A 或 当前节点为A
- 当前树是否有B = 左子树有B 或 右子树有B 或 当前节点为B
- 该题目的核心思路就是比上题多出两个点的flag
- 最后返回结果时要判断A和B是否都存在
3.2 二叉树结构变化
LeetCode 114. 二叉树展开为链表
要点:DFS前序遍历这棵树,然后把结果一路向右串联起来
思路:树的链表 = 树的根节点 + 左子树的链表 + 右子树的链表
- 如果想重组连接这三个部分,需要得到左右子树链表的头部节点和尾部节点。左右子树的头部节点可以由root.left和root.right获得。因此定义该递归函数,返回当前这棵树摊平后的尾部节点。
- 如果左子树不为空,则需要重组root节点、左子树链表和右子树链表
- 如果左子树为空,不需要重组,root.right连接的就是右子树链表
3.3 二叉查找树 Binary Search Tree
1)概述
- 左子树节点的值 < 根节点的值 < 右子树节点的值
- BST中的任意一个子树都是BST
- 中序遍历结果有序,是非递减的
2)红黑树 Red-Black Tree
- 红黑树是一种Balanced BST
- Java的API实现 TreeMap / TreeSet
- O(logN)的时间内实现增删查改;实现找最大最小
3)相关例题 LeetCode
108. 将有序数组转换为二叉搜索树, 701. 二叉搜索树中的插入操作, 700. 二叉搜索树中的搜索, 剑指 Offer II 055. 二叉搜索树迭代器
LeetCode 230. 二叉搜索树中第K小的元素
思路:
- BST的中序遍历是有序的,非递减的
- 因此采用二叉树中序遍历的非递归实现,找到答案停止,无需遍历完整棵树
LintCode 900 · 二叉搜索树中最接近的值
思路:该题的核心思想就是找到目标值的最小上边界upperbound和最大下边界lowerbound
- 无优化的解法:中序遍历得到有序数组,然后进行二分查找
- 优化解法:利用bst的左子树节点 < 根节点 < 右子树节点的特性,不断更新上下边界,从而找到最接近的值
LintCode 901 · 二叉搜索树中最接近的值 II
思路一:
- 利用BST中序遍历的特性,得到一个有序列表list
- 转化为在list上做二分法,找到离target最近的k个数
- 时间复杂度为O(n)+O(logn)+O(k)
思路二:
- 依然利用了BST中序遍历有序的特性,使用非递归实现二叉树的中序遍历 (保存根节点到当前节点的整条路径的实现方式)
- 通过不断比较当前节点值与target,保存了一路走过的节点到两个栈lowerStack和upperStack里
- 通过比较target离两个栈顶节点的值哪个更近,来将lowerStack挪动到prev node或将upperStack挪动到next node
- 时间复杂度是O(h)+O(k)