文章目录
三、树与二叉树
这篇总结的主要是栈和队列的知识点,可以收藏下来,哪些地方概念不是特别清楚的时候翻出来看看。如果想看本章涉及到的代码部分,见文章下方超链接。
另外这一部分总结的有点乱,都是干巴巴的知识,有些地方还是很难理解的,如需讨论的,随时评论区可以讨论。
(一)树的基本概念
-
树的定义
- 树是一种非线性的数据结构。
- 它是由若干个结点构成的集合(树是n>=0个结点的有限集),由唯一的根和若干棵互不相交的子树组成的。每一个子树又是一棵树,同样它也是由唯一的根结点和若干棵互不相交的子树组成。
- 树的结点数为0,称之为空树。
-
树的基本术语
- 结点:A,B,C都是结点,结点不仅包含数据元素,而且包含指子树的分支。例如A结点,不仅包含数据元素A,还包含3个指向子树的分支。
- 结点的度:结点拥有的子树的个数或者分支的个数。
- 树的度:树中各结点度的最大值。
- 叶子结点:又叫做终端结点,度为0的结点。
- 非终端结点:又叫做分支结点,度不为0的结点。
- 孩子结点:结点的子树的根。例如A的孩子结点为B,C,D
- 双亲结点:与孩子结点定义对应,B,C,D的双亲都是A结点
- 兄弟结点:同一个双亲的孩子之间互为兄弟。如B,C,D为兄弟结点。
- 堂兄弟结点:双亲在同一层的结点互为堂兄弟。如G和H为堂兄弟,其它们的双亲为C和D在同一层上。
- 祖先:从根到某一个结点路径上所有的结点,这是这个结点的祖先。
- 子孙:以某结点为根的子树中的所有结点。
- 层次:从根开始,根为第一层,根的孩子为第二层,以此类推。
- 树的高度:树种结点的最大层次。
- 结点的深度:从根结点到该结点路径上的结点个数。
- 结点的高度:从某结点往下走可能到达多个叶子结点,对应了多条路径,其中最长的路径即为结点的高度。例如结点D的高度为3,也就是D到M的路径长度。根结点的高度就是树的高度。、
- 有序树:树中结点的子树从左到右是有次序的,不能交换。
- 无序树:树中结点的子树没有顺序,可以任意交换。
- 丰满树:是理想平衡树,要求除最底层外,其他层都是满的。
- 森林:若干棵互不相交的树的集合。
(二)二叉树
-
二叉树的定义
- 二叉树是另一种树型结构,是在一般的树上加上两个限制条件就得到了二叉树。
- 每个结点最多有两棵子树,即二叉树中,每个结点的度最大是2,可以是0,1,2
- 子树有顺序之分,不能颠倒
- 满二叉树:如果所有的结点都有左孩子和右孩子,并且叶子结点都集中在二叉树的最下一层。
- 完全二叉树:将二叉树进行编号,从上到下,从左到右,此二叉树与满二叉树再相同位置的结点的编号相同,即为完全二叉树。完全二叉树没有要求所有结点都必须有左孩子和右孩子,只要求与满二叉树编号相同即可。
- 通常来说,一棵完全二叉树一定是由一棵满二叉树从右到左,从下到上,挨个删除结点得到的。
- 二叉树是另一种树型结构,是在一般的树上加上两个限制条件就得到了二叉树。
-
二叉树的性质
-
非空二叉树上的叶子结点数等于双分支结点数加1。
-
二叉树的第 i 层上最多有2^(i-1) (i >= 1)个结点。
-
高度为k的二叉树最多有2^ k - 1 (k >= 1)个结点。
-
函数Catalan() 给定n的结点,能构成h(n)种不同的二叉树。
h ( n ) = C 2 n n n + 1 h(n) = \frac{C_{2n}^{n}}{n+1} h(n)=n+1C2nn -
具有n (n >= 1)的结点的完全二叉树,高度为
⌊ l o g 2 n + 1 ⌋ 向 下 取 整 \lfloor log_2n +1\rfloor 向下取整 ⌊log2n+1⌋向下取整 -
有n个结点的完全二叉树,对各结点从上到下,从左到右进行编号。 若 i 为某结点 a 的编号,i ≠ 1 则 a 的双亲结点的编号为
⌊ i / 2 ⌋ 向 下 取 整 \lfloor i/2\rfloor 向下取整 ⌊i/2⌋向下取整-
如果 2i ≤ n,则 a 的左孩子的编号为 2i , 如果 2i > n , 则 a 无 左孩子
-
如果 2i ≤ n,则 a 的右孩子的编号为 2i + 1 ,如果 2i > n , 则 a 无 右孩子
-
-
-
二叉树的顺序存储结构和链式存储结构
- 顺序存储结构:
用一组地址连续的存储单元依次自上而下,自左而右存储完全二叉树上的元素。(最适合存储完全二叉树) - 链式存储结构:
- 观察二叉树的形态,发现是由一个根节点与两棵子树之间的关系,因此用一个数据域和两个指针域的链式存储结构。
- 数据域用来存储结点的数据,指针域用来存储左右孩子的结点的位置。
- 这种存储结构称为二叉链表存储结构。
- 顺序存储结构:
-
二叉树的遍历
- 先(根)序遍历
- 若二叉树为空,则空操作。否则
- 访问根结点
- 先序遍历左子树
- 先序遍历右子树
- 中(根)序遍历
- 若二叉树为空,则空操作。否则
- 中序遍历左子树
- 访问根结点
- 中序遍历右子树
- 后(根)序遍历
- 若二叉树为空,则空操作。否则
- 后序遍历左子树
- 后序遍历右子树
- 访问根结点
- 层次遍历
- 按层遍历,每层从左往右或从右往左挨个遍历
- 先(根)序遍历
-
线索二叉树的基本概念和构造
-
首先明白两个概念:
- 直接前驱:当前结点的前一个结点,
- 直接后继:当前结点的后一个结点
-
n个结点的二叉链表中,有n+1个指针域为空
因为:n个结点,每个结点有两个指针域(左右孩子指针域),共有2n个指针域;然而,n个结点中,有n-1个孩子(根结点不是孩子),所以2n个指针域中,有n-1个指针域用来表示结点的左右孩子,其余n+1个指针域为空。
-
何为线索?
如果某个结点的左孩子为空,则将空的左孩子指针域指向其前驱;如果某结点的右孩子为空,则将空的右孩子指针域指向其后继。这种改变指向的指针称为线索。
-
线索二叉树
加上了线索的二叉树叫做线索二叉树
-
线索化
对二叉树按照某种遍历次序使其为线索二叉树的过程叫做线索化。
-
线索二叉树结点的结构如下
- Ltag = 0表示lchild为指针,指向左孩子结点;Ltag = 1表示lchild为线索,指向结点的直接前驱
- Rtag = 0表示rchild为指针,指向右孩子结点; Rtag = 1表示rchild为线索,指向结点的直接后继
前驱或者左子树 0左孩子,1前驱 数据 0右孩子,1后继 后继或者右子树 lchild Ltag data Rtag rchild - 线索二叉树又有:前序线索二叉树,中序线索二叉树,后序线索二叉树。中序二叉树考查最频繁。对一个二叉树所有的空指针域按照某种遍历方式加线索的过程称为线索化。
-
(三)树、树林
-
树的存储结构
- 顺序存储结构
- 树的顺序存储结构双亲表示法——双亲存储结构
- 用数组下标表示树中的结点,用数据元素表示该结点的双亲结点。这样就有了结点以及结点之间的关系,就可以表示一棵树了。
- 链式存储结构
- 树的链式存储结构孩子表示法——孩子存储结构
- 树的链式存储结构孩子兄弟表示法——孩子兄弟存储结构
- 树的顺序存储结构双亲表示法——双亲存储结构
- 顺序存储结构
-
森林与二叉树的转换
-
树转换为二叉树
1、将同一结点的各孩子用线串起来,即所有兄弟结点之间2、将每个结点的分支从左到右除了第一个以外,其余都剪掉
3、调整结点位置,使其符合二叉树的层次结构。
-
二叉树转换为树
1、先将其从左上到右下分为若干层,然后调整为水平方向
2、找到每一层结点在其上一层的父结点,并且将每一层的结点与其父结点相连接
3、然后删除每一层结点之间的连线,即兄弟结点之间的连线
4、调整位置
-
森林转化为二叉树
1、先将森林中的多棵树分别转化为二叉树
2、将第二棵二叉树作为第一棵二叉树的右孩子,第三棵二叉树作为第二棵二叉树的右孩子,以此类推。(图中蓝线部分)
3、调整位置
-
二叉树转化为森林
1、不断的将根节点有右孩子的二叉树断开,直到不存在根结点有右孩子的二叉树为止。
2、将断开的树调整位置成森林
-
-
树和森林的遍历
- 树的遍历
- 先序(根)遍历
先访问树的根节点,然后再依次访问根结点的每棵子树,访问子树时仍然遵循先根再子树的规则。 - 后序(根)遍历
先依次根结点遍历每棵子树,然后再访问根结点,访问子树时仍然遵循先子树再根的规则。
- 先序(根)遍历
- 森林的遍历
- 先序(根)遍历
先访问森林中第一棵树的根节点,然后再先序遍历第一棵子树中的根结点的每棵子树,最后先序遍历森林中除第一棵子树外的其他树。 - 后序(根)遍历
后序遍历第一棵树中的根结点的子树,然后访问第一棵树的根结点,最后后序遍历森林中除第一棵树以后的其他树。
- 先序(根)遍历
- 树的遍历
(四)树与二叉树的应用
-
二叉排序树——详细请看这里
- 二叉排序树又称二叉查找树。具有以下性质:
- 若左子树非空,则左子树上所有结点关键字值均小于根结点的关键字值
- 若右子树非空,则右子树上所有结点关键字值均大于根结点的关键字值
- 左、右子树各是一棵二叉排序树
由此定义可知,二叉排序树是一个递归的数据结构,有左子树结点值<根结点值<右子树结点值。所以,对二叉排序树进行中序遍历,可以得到一个递增的有序序列。
- 二叉排序树又称二叉查找树。具有以下性质:
-
平衡二叉树(AVL树)——详细请看这里
- 任意结点的左、右子树高度差的绝对值不超过1,将这样的二叉树称为平衡二叉树,简称平衡树(AVL树)。
- 一个结点的平衡因子为左子树的高度减去右子树的高度。因此平衡因子取值只能是1,-1,0.
-
赫夫曼(Huffman)树和赫夫曼编码
-
赫夫曼树
-
赫夫曼树又叫做最优二叉树,特点是带权路径最短。什么是路径?什么是最短路径?
- 路径:指从树中的一个结点到另一个结点的分支所构成的路径。
- 路径长度:路径长度是指路径上分支的数目。——a 结点到根结点分支数目为2,所以路径长度为2.
- 树的路径长度:指从根到每个结点的路径长度之和。
- 带权路径长度:结点具有权值,从该结点到根结点之间的路径长度乘以结点的权值。——a的带权路径长度为7*2=14
- 树的带权路径长度(WPL):树种所有的叶子结点带权路径之和。——这棵树的带权路径长度为WPL=7 * 2+5 * 2 + 2 * 2 + 4 * 2 = 38
-
赫夫曼树的构造方法
1、 先将A,B,C,D看成只有根的4棵二叉树
2、选出权值最小的A,C两个结点,将他们作为左右子树构造除一棵新的二叉树,新的二叉树的根结点权值为A ,C 的权值之和,删除A,C两个结点,同时将新构造的二叉树加入集合中。
3、继续选择两个权值最小的树,按照同样的方法构造二叉树。依次类推
4、此时,集合中就剩下了一棵二叉树,这棵二叉树就是赫夫曼树
-
赫夫曼树的特点
- 权值越大的结点,距离根结点越近
- 树种没有度为1的结点,这类书又叫做正则(严格)二叉树
- 树中带权路径长度WPL最短
-
-
赫夫曼编码
-
赫夫曼编码是一种可变字长的编码。
-
如果一组编码中,任一编码都不是其他任何一个编码的前缀,我们称这组编码为前缀编码。赫夫曼树可用于构造最短的不等长编码方案。
-
规定赫夫曼编码的树左分支代表0,右分支代表1,则从根结点到每个叶子结点所经过的路径组成的0和1序列便成为了该叶子结点对应字符的编码。如下:
-
哈夫曼编码树中,树的带权路径长度的含义是各个字符的码长与其出现次数的乘积之和,所以采用哈夫曼树构造的编码是一种能使字符串的编码总长度最短的不等长编码。
-
-
-