一、二叉树
1、常见名词
DFS (depth first search ) 深度优先遍历
BFS (breadth first search ) 广度优先遍历
BST (binary search tree) 二叉搜索树
AVL(Adelson-Velsky and Landis) 平衡二叉搜索树
heap 堆
2、常见分类
-
满二叉树:是一种特殊的完全二叉树。只有度为0的节点和度为2的节点,且度为0的节点在同一层
- 什么是二叉树的度:就是二叉树中 节点和 节点 的线,有 n 个节点,就有 n-1 个度
- 深度为 k 的 满二叉树的节点数量:2^k-1
-
完全二叉树:除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。
-
二叉搜索树:前面两种都是没有数值的,这个是有数值的树。且是有序树
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
- 即 大小顺序 就是 左 中 右
-
平衡二叉搜索树: 又被称为AVL(Adelson-Velsky and Landis)树
- 在满足二叉搜索树的前提下:
要么是一棵空树,
要么它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树(所谓高度,即层数)
平衡二叉搜索树的增删操作的时间复杂度都是 logN
-
红黑树
- 是 自平衡的二叉搜索树
- 把树中的结点定义为红黑两种颜色,并通过规则确保从根节点到叶节点的最长路径的长度不超过最短路径的两倍。
- set、map等数据结构都是基于红黑树实现的。
-
堆
- 可分为最大堆和最小堆,也就是大顶堆、小顶堆。
- 最大堆中根节点的值最大,最小堆中根节点的值最小。
-
前缀树 字典树 Trie
- 是一颗非典型的多叉树模型,多叉好理解,即每个结点的分支数量可能为多个。
- 力扣 第208题
3、常见应用
C++ 中 map、set、multimap、multiset的底层实现都是平衡二叉搜索树,所以他们的增删操作的时间复杂度都是 logN。
(unordered_set 和 unordered_map)的底层实现是哈希表,增删操作的时间复杂度为 1。
4、二叉树存储方式
// 链式存储的二叉树的节点定义方式
struct TreeNode
{
int val ;
TreeNode* leftChild ;
TreeNode* rightChild ;
**// 注意初值列是用小括号幅值,而不是等于号哦!!!!**
TreeNode (int x) : val (x) , left (NULL), right (NULL) {}
}
5、二叉树的遍历(与图论中的最基本的两种遍历方式相同)
-
1、DFS depth first search 深度优先遍历:先忘深度走,遇到叶子节点再往回走。
- 前序遍历(前中后指叶子节点)
- 中序遍历
- 后续遍历
- 前序遍历(前中后指叶子节点)
-
2、BFS breadth first search 广度优先遍历:一层一层的去遍历。
- 层次遍历
6、遍历方式的实现
栈的应用: 其实就是一种实现递归的结构,因此前中后序遍历的逻辑可以借助栈,来实现 非递归 的方式。
队列的应用: 层序遍历的实现一般是使用队列,因为队列先进先出的特点,巧好是实现层次遍历所需要的。
递归在本质上就是一种栈的结构,如果可以用栈来实现,自然而然就想到可以用递归来实现。
递归代码虽然看起来简洁,但是当链表非常长的时候,就会导致函数调用的层级很深,从而有可能导致函数调用栈溢出。因此用栈基于循环实现的代码的鲁棒性要好一些。
1、递归法
- 前序遍历:根节点,递归左子树,递归右子树,终止条件是 节点为空。
- 中序遍历:递归左子树,根节点,递归右子树,终止条件是 节点为空。
- 后序遍历:递归左子树,递归右子树,根节点,终止条件是 节点为空。
2、迭代法
- 1、前序遍历:
* - 2、中序遍历
* - 3、后序遍历
* - 4、层次遍历
*
7、用迭代法遍历的代码实现,有两种
* 1)第一种迭代实现
处理前序遍历和后序遍历比较相似,也比较直观,但是中序遍历就跟这两种的思路不同,由于它遍历和访问的顺序不同导致的结果
在用迭代法遍历树的时候,其实是有两个操作
1)处理节点:即将元素放进 result 数组中,处理的过程即出栈
2)访问节点:即遍历节点,访问的过程即压栈
对于前序遍历,其顺序是 中左右,先访问的元素是中间节点,先处理的元素也是中间节点,由于访问元素和处理元素的顺序是一致的,都是中间节点,所以可以比较便捷
对于中序遍历,其顺序是左中右,先访问的是根节点,然后一层一层向下访问,直到达到树左面的最底部,在开始处理节点(也就是在把节点的数组都放进 result 数组中),这造成了处理顺序和访问顺序是不一致的。 因此在使用迭代法写中序遍历时,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
对与后序遍历,其顺序为左右中,是根据前序遍历改成的。
——>先序遍历:中左右——>调整左右顺序:中右左——>反转数组——>左右中
- 1、前序遍历:先将跟节点放入栈中,然后将右孩子加入栈,再加入左孩子。
- 为什么要先加入 右孩子,