赞
踩
树既可以采用顺序存储结构,又可采用链式存储结构。但无论采取哪种方式,都要求能够唯一地反映树中各结点之间的逻辑关系。
这种存储结构采用一组连续空间来存储每个结点,同时在每个结点增设一个伪指针,指示其双亲结点在数组中的位置。
#define MAX_TREE_SIZE 100
typedef struct ElemType {
int value;
}ElemType;//预处理
typedef struct {
ElemType data;
int parent;//双亲位置域
}PTNode;
typedef struct {//树的类型定义
PTNode nodes[MAX_TREE_SIZE];//双亲表示
int n;//结点数
}PTree;
双亲表示法利用了除根结点以外每个结点只有唯一双亲的性质,优点是可以很快地得到每个结点的双亲结点,但缺点也很明显,求结点的孩子时需要遍历整个结构。
使用双亲表示法存储树时,删除结点共有两个方法:
n--
。这种方法要优于方法一。孩子表示法是将每个结点的孩子视为一个线性表,且以单链表作为数据结构,这样 n n n个结点就有 n n n个孩子链表(叶结点的孩子链表视为空表)。
struct CTNode {//单链表(B站弹幕说是邻接表)
int child;//孩子节点在数组中的位置
struct CTNode* next;//下一个孩子
};
typedef struct {
ElemType data;
struct CTNode* firstChild;//第一个孩子
}CTBox;
typedef struct {
CTBox nodes[MAX_TREE_SIZE];//孩子表示法
int n, r;//结点数和根的位置
}CTree;
与双亲表示法相反,孩子表示法寻找结点孩子的操作非常方便,但是寻找双亲的操作则需要遍历 n n n个结点中孩子链表指针域所指向的 n n n个孩子链表。同样的,可以顺着这个思路思考,如何实现孩子表示法存储的树的增删查改等基本操作。
孩子兄弟表示法又称二叉树表示法,即以二叉链表作为树的存储结构。孩子兄弟表示法使每个结点包括三部分内容:结点值、指向结点第一个孩子结点的指针,以及指向结点下一个兄弟结点的指针。
typedef struct CSNode {//孩子兄弟表示法
ElemType data;//数据域
struct CSNode* firstchild, * nextsibling;//第一个孩子和右兄弟指针
}CSNode, * CSTree;
使用这种方法最大的优点就是可以实现将树转换为二叉树的操作,优缺点与二叉树的链式存储结构相同,这里不再展开。
从物理结构上看,树的孩子兄弟表示法和二叉树的二叉链表表示法是相同的。因此可以用同一存储结构的不同解释将一棵树转换为二叉树。
树转换为二叉树的规则:每个结点的左指针指向它的第一个孩子,右指针指向它在树中的相邻右兄弟,这个规则也称“左孩子右兄弟”。由于根结点没有兄弟,因此树转换得到的二叉树没有右子树,如图所示。 (图片来自王道考研408数据结构2025)
树转换为二叉树的画法:
①在兄弟结点之间加一条线;
②对每个结点,只保留它与第一个孩子的连线,与其他孩子的全部抹掉;
③以树根为轴心,顺时针旋转45°。
二叉树转换为树逆着来就行。
森林转换为二叉树的规则与树类似,只需要把每一棵树的根结点都看成兄弟,依次连在第一棵树根结点的右子树上即可。
森林转换为二叉树的画法:
①将森林中的每棵树转换成相应的二叉树;
②每棵树的根也可视为兄弟关系,在每棵树的根之间加一根连线;
③以第一棵树的根为轴心,顺时针旋转45°。
二叉树转换为森林同样逆着来就行,这里就不再展开。
树的遍历是指用某种方式访问树中的每个结点。主要有两种方法:
void Visit(CSNode* T) { cout << T->data.value << " "; return; } void PreOrder(CSTree T) {//先根遍历(使用孩子兄弟表示法) if (T != NULL) { Visit(T);//访问根结点 CSTree C = T->firstchild;//C记录当前树根结点的第一个孩子 do {//首先对以C为根的子树进行先根遍历 PreOrder(C); C = C->nextsibling;//再依次对C的右兄弟为根的子树进行先根遍历 } while (C != NULL);//当C还有其他右兄弟时循环继续 } return; }
其遍历序列与这棵树对应的二叉树的先序序列相同。
void PostOrder(CSTree T) {//后根遍历
if (T != NULL) {
CSTree C = T->firstchild;
do {
PostOrder(C);//对以C为根的子树后根遍历
C = C->nextsibling;//依次访问C的右兄弟结点
} while (C != NULL);
Visit(T);//最后访问根结点
}
return;
}
其遍历序列与这棵树对应二叉树的中序序列相同。
例如,前文中图
5.23
5.23
5.23中的树,先根遍历序列为
A
B
E
F
C
D
G
ABEFCDG
ABEFCDG,后根遍历序列为
E
F
B
C
G
D
A
EFBCGDA
EFBCGDA。
基本思想与二叉树的层次遍历相同。首先构造辅助队列;根结点入队;队头元素出队,访问当前结点,再将该结点的所有孩子结点入队;依次类推,直到队列为空。
由于森林和树相互递归的定义,可以得到森林的两种遍历方法:
其实就是按顺序对森林里每棵树依次进行先根遍历,然后把遍历序列按顺序连起来;或者把森林转换成二叉树再进行先序遍历。
其实就是按顺序对森林里每棵树依次进行后根遍历,然后把遍历序列按顺序连起来;或者把森林转换成二叉树再进行中序遍历。
对树的遍历,层次遍历又被称为广度优先遍历,先根、后跟又被称为深度优先遍历。
408考研初试中,需掌握手推树与森林的遍历序列的能力,如果运气太差碰到代码实现题,可以先转换成二叉树,再用熟悉的方法处理问题。
以上。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。