[入门必看]数据结构5.4:树、森林
- 第五章 树与二叉树
- 5.4 树、森林
- 知识总览
- 5.4.1 树的存储结构
- 5.4.2 树、森林与二叉树的转化
- 5.4.3 树和森林的遍历
- 5.4.1 树的存储结构
- 树的逻辑结构
- 回顾:二叉树的顺序存储
- 如何实现树的顺序存储?
- 树的存储1:双亲表示法
- 拓展:双亲表示法存储“森林”
- 双亲表示法的优缺点
- 树的存储2:孩子表示法
- 拓展:孩子表示法存储“森林”
- 孩子表示法的优缺点
- 树的存储3:孩子兄弟表示法
- 拓展:孩子兄弟表示法存储“森林”
- 5.4.2 树、森林与二叉树的转化
- 树->二叉树的转换
- 森林->二叉树的转换
- 二叉树->树的转换
- 二叉树->森林的转换
- 5.4.3 树和森林的遍历
- 树的逻辑结构
- 树的先根遍历
- 树的后根遍历
- 树的层次遍历
- 森林的先序遍历
- 森林的中序遍历
- 知识回顾与重要考点
- 5.4.1 树的存储结构
- 5.4.2 树、森林与二叉树的转化
- 5.4.3 树和森林的遍历
第五章 树与二叉树
小题考频:30
大题考频:8
5.4 树、森林
难度:☆☆☆
知识总览
5.4.1 树的存储结构
5.4.2 树、森林与二叉树的转化
5.4.3 树和森林的遍历
5.4.1 树的存储结构
树的逻辑结构
树是一种递归定义的数据结构
一棵树要么是空树,要么至少有一个根结点,根结点的下方可能有零棵或多棵互不相交的子树。
二叉树:一个分支结点最多只能有两棵子树
树:一个分支结点可以有多棵子树
如何用顺序存储来存储树这种数据结构?
回顾:二叉树的顺序存储
二叉树的顺序存储:按照完全二叉树中的结点顺序,将各结点存储到数组的对应位置。数组下标反映结点之间的逻辑关系
顺序存储二叉树时,结点编号会和完全二叉树对应。
那么这种思路能否推广到普通的树?
如何实现树的顺序存储?
由于每个分支结点的子树数量不确定,所以没有与之对应的“完全树”,单纯依靠数组下标无法判断结点之间的逻辑关系。
可以发现树中,除了根结点外,其他任意结点都有且只有一个双亲结点(父结点)。
思路:用数组顺序存储各个结点。每个结点对应一个数组下标,在其中保存数据元素、指向双亲结点(父结点)的“指针”
树的存储1:双亲表示法
数组元素中,包含两个字段,第一个字段表示数据元素,第二个字段用一个int型变量指明双亲在数组中的下标是多少。
用刚才定义的结构体声明一个数组。
最后记录结点的总数。
拓展:双亲表示法存储“森林”
双亲表示法的优缺点
优点:找双亲(父结点)好
缺点:找孩子难,要遍历整个数组
应用场景:找父亲多,找孩子少,Eg.并查集
树的存储2:孩子表示法
在每个数组在链表元素中,保留一个链表头指针,如果其有孩子,在链表中保存所有孩子结点的编号
孩子表示法是顺序存储+链式存储结合
一个数组元素中包含data和一个链表CTNode的指针,链表结点中保存孩子的编号以及指向下一个链表结点的指针。
用刚才定义的结构体CTBox声明一个数组,存储各结点的信息。
并记录结点总数和根的位置。
拓展:孩子表示法存储“森林”
用孩子表示法存储森林,需要记录多个根的位置
孩子表示法的优缺点
优点:找孩子好
缺点:找双亲(父结点)难,要遍历整个数组
应用场景:找孩子多,找父亲少,Eg.服务流程树【办理业务请按1】
树的存储3:孩子兄弟表示法
链式实现
结构体包含数据,和两个指针,第一个指针指向第一个孩子,第二个指针指向右边一个兄弟【类似二叉树结点的定义】
采用二叉链表实现
根结点是A,左指针指向B,右指针指向NULL;
C是B的右边第一个兄弟结点,连到B的右指针,D结点连到C的右指针;
E是B的第一个孩子,连到B的左指针;
F是E的右边第一个兄弟结点,连到E的右指针,G结点连到F的右指针,H连到G的右指针,I连到H的右指针,J连到I的右指针;
K是E的第一个孩子,连到E的左指针。
孩子兄弟表示法形态和二叉树类似。
拓展:孩子兄弟表示法存储“森林”
把树根C看做B的兄弟,连到B右指针上,以此类推。
5.4.2 树、森林与二叉树的转化
树->二叉树的转换
用孩子兄弟表示法存储树或者森林的时候会呈现出和二叉树类似的形态。
树、森林与二叉树的转换本质上就是画出用孩子兄弟表示法表示的树和森林。
树 -> 二叉树转换技巧:
① 先在二叉树中,画一个根结点。
② 按**“树的层序”**依次处理每个结点。
处理一个结点的方法是:如果当前处理的结点在树中有孩子,就把所有孩子结点“用右指针串成糖葫芦”,并在二叉树中把第一个孩子挂在当前结点的左指针下方
Eg1:
A结点:
----->
B结点:
----->
C结点:
H结点:
Eg2.
森林->二叉树的转换
思路同上。
注意,第一步:森林中各树的根结点视为兄弟,用右指针串起来。
Eg2.
二叉树->树的转换
还原A结点:
以此类推还原。
Eg2.
二叉树->森林的转换
原理类似。
注意,先将根节点和一串右指针还原成多棵树的根结点。
Eg2.
5.4.3 树和森林的遍历
树的逻辑结构
有递归特性,可以用递归算法来实现对树的遍历
树的先根遍历
访问根结点
visit(R)
,接下来用while循环来检查其还有没有下一个没有被访问过的子树。
如果有,那么对子树也采取先根遍历的策略。
树的先根遍历序列与这棵树相应二叉树的先序序列相同。
树的后根遍历
原理类似,处理完所有子树时访问结点。
树的后根遍历序列与这棵树相应二叉树的后序序列相同。
树的层次遍历
实现思路和二叉树一样,用一个辅助队列来实现
根结点入队:
队头元素出队,该元素的孩子依次入队
重复该过程
尽量横向探索,也叫广度优先遍历。
那么先根遍历和后根遍历,也叫深度优先遍历。
森林的先序遍历
森林去掉根结点后各子树又组成森林,森林这种数据结构是和树相互递归定义的
可以用递归的思想来遍历森林
首先,访问森林中第一棵树的根结点B
先序遍历第一棵树中根结点的子树森林,E、F两棵树组成的森林
重复第一步,先访问森林中第一棵树的根结点E
遍历E的两个子树森林,即K、L两棵树组成的森林
遍历完K后,再遍历除了第一棵树K之后的剩余的树构成的森林,即L
效果等同于依次对各个树进行先根遍历
可以对各个子树进行一个先序遍历,然后排出一个完整的序列
另一种方法:转换成与之对应的二叉树:
效果等同于依次对二叉树的先序遍历
森林的中序遍历
同样可以转换成与之对应的二叉树:
效果等同于依次对二叉树的中序遍历
知识回顾与重要考点
5.4.1 树的存储结构
- 双亲表示法:顺序存储,保存父结点下标,易找父,难找孩
- 孩子表示法:顺序存储,保存孩子链表头指针(顺序+链式),易找孩,难找父
- 孩子兄弟表示法:二叉链表存储,左孩子右兄弟,形态上和二叉树类似
重要考点:树、森林与二叉树的转换
5.4.2 树、森林与二叉树的转化
- 本质上就是用孩子兄弟表示法存储树或森林
- 存储森林时,将每棵树的根结点视为兄弟
- Tips:记“本质”,不记方法。方法只是表象,“本质”才是内核。
5.4.3 树和森林的遍历
- 对森林的遍历可以转换成对二叉树的遍历