注: 本文的对各个概念的定义不一定采用常用的标准定义, 原因有二:
- 常用的定义描述在百度百科上即可查到
- 更多的是想通过自己的理解, 用简短的几个关键词语或句子, 来点明核心要点
二叉树
基本概念和性质
二叉树的定义: 二叉树中的每个节点至多有2棵子树, 并且子树有左右之分, 其次序不能任意颠倒。
二叉树的性质:
- 对于非空二叉树: $N_0 = N_2 +1$
- 在于非空二叉树, 第k层上的节点数不超过: $2^{k-1}$
- 高为h的二叉树, 总节点数不超过: $2^h-1$
- 具有N个节点的完全二叉树, 其高度为: $\lceil log_2(N+1) \rceil$ 或 $\lfloor log_2{N} \rfloor +1$
- 对于完全二叉树, 如果各个节点按照顺序存储(从 1 开始), 则节点之间编号具有一定关系:
- 节点 $i$ 的父节点为 $\lfloor \frac{i}{2} \rfloor (i>1)$
- $i$的左右子树分别为: $2i$ 和 $2i+1$ (如果$2i/2i+1 \geq N$, 则无左/右子树
- 给定N个节点, 能够成$h(N)$ 种不同的二叉树: $h(N)=…$
- 设有$i$个枝点, $I$为所有枝点的道路长度总和, $J$为叶的道路长度总和$J=I+2i$
注意: 二叉树不是度为2的树。 度为2的树至少要有3个节点, 而二叉树可以为空;度为2的树的左右子树是相对而言的, 当只有一个孩子时, 就无须分左右
满二叉树与完全二叉树
满二叉树: 除叶子节点外的所有节点均有两个子节点
完全二叉树: 最后一个不满的“满二叉树”, 最后一层所有节点集中在左边
二叉树的遍历
先根遍历: 根节点、左子树、右子树
递归实现
1 | void preOrder(Tree t){ |
非递归实现
对于任一节点, 其可看做是根节点, 因此直接访问, 访问后, 若其左孩子不为空, 则按相同规则访问其左子树, 当访问完左子树之后, 再访问其右子树:
对于任一节点P:
- 访问节点P, 并将P入栈
- 如果P的左孩子不为空, 则令P = P->left, 并转向第一步。若为空, 则将P出栈, 并令P = P->right, 然后转向第一步。
- 直到P为nullptr并且栈为空时, 结束循环
1 | vector<TreeNode*> preOrder; |
中根遍历: 左子树、根节点、右子树
递归
1 | void in_order(TreeNode* pRoot){ |
非递归
对于任一节点, 优先查看其左孩子, 而左孩子节点又可以看作是一个根节点, 则继续优先查看其左孩子, 直到遇到左孩子节点为空的根节点才进行访问。然后再转向查看其右孩子, 右孩子可看作是一个根节点, 继续按上面的规则循环:
对于任一节点P:
- 若其左孩子不为空, 则就P入栈并将P的左孩子置为当前的P, 然后继续查看当前P的左孩子, 直到为空;
- 经过上一步后, P指向了空的左孩子, 因此取栈顶元素并进行出栈操作, 同时访问该栈顶节点, 然后将P置为栈顶节点的右孩子(无需判断右孩子是否为空, 若为空则下一次循环会自动继续取栈顶)
- 知道P为nullptr并且栈为空时循环结束
1 | vector<TreeNode*> inOrder; |
后根遍历: 左子树、右子树、根节点
递归
1 | void in_order(TreeNode* pRoot){ |
非递归
后序遍历的非递归实现是最难的一种。因为在后序遍历中, 要保证左孩子和右孩子都已被访问, 并且左孩子在右孩子之间访问, 最后才能访问根节点。有两种思路:
思路一:
思路二:
二叉排序树(Binary Sort Tree, BST)
基本概念和性质
定义: 也叫二叉查找树或有序二叉树。当树不为空时, 该树具有如下性质:
- 左子树上的所有节点值, 均小于其根节点的值
- 右子树上的所有节点值, 均大于其根节点的值
- 左、右子树也分别为二叉排序树
- 没有键值相等的节点
性质: 对二叉排序树进行中根遍历, 即可得到一串有序数列(从小到大)
时间复杂度: 插入与查找的复杂度均为$O(logn)$, 但在最坏情况下为$O(n)$(原因在于树不一定是平衡的)
平衡二叉树
AVL树, 名字来源于其发明者 Adelson-Velsky 和 Landis
红黑树
B+树
字典树
基本概念
Trie 树, 又称字典树, 单词查找树或前缀树, 是一种用于快速检索字符串的多叉树结构, 如英文字母的字典树是一个26叉树, 数字的字典树是一个10叉树.
字典树可以利用字符串的公共前缀来节约空间, 如下图所示.
当然, 如果字符串列表中存在大量没有公共前缀的字符串, 则字典树会变得非常消耗内存. 字典树的基本性质可以概括为以下三点:
- 除根节点以外, 每个节点都只包含一个字符(根节点不包含字符)
- 当前节点的所表示的字符串, 就是将从根节点到当前节点的路径上的字符连接起来的字符串.
- 每个节点的所有子节点包含的字符各不相同
基本实现
http://dongxicheng.org/structure/trietree/