树的表示方法
双亲表示法
用一组地址连续的存储单元来存放树中的各个节点,每一个节点中有一个数据域和一个指针域,数据域用来存储树中该节点本身的值;另一个指针域用来存储该节点的双亲节点在存储结构中的位置信息。
采用双亲链表存储方式实现查找一个指定节点的双亲节点比较方便,但难以实现查找一个指定节点的孩子节点。
孩子表示法
用一组地址连续的存储单元来存放树中的各个节点,每一个节点有一个数据域和一个指针域,数据域用来存储树中该节点的值;另一个指针域用来存放该节点的孩子链表的头指针。形式上有点像存储图的领接表。
采用孩子表示法便于实现查找树中指定节点的孩子节点,但难以实现查找树中指定节点的双亲节点。
孩子兄弟表示法
孩子兄弟表示法是用一种链式的结构存储一颗树的方式。每个节点含有孩子指针域和兄弟指针域。我们分别用 firstchild 和 nextsibling 表示。
其中孩子指针域,表示指向当前结点的第一个孩子结点,兄弟结点表示指向当前结点的下一个兄弟结点。其本质是先将一棵树转换为二叉树后存储在二叉链式结构之中。
孩子兄弟表示法能够将对一棵树的操作转换成对二叉树的操作,这种表示方法在实际运用中比较广泛。
故在这里给出孩子兄弟表示法创建树的代码,在后文中也都是使用这种表示方法。
孩子兄弟表示法创建树
typedef char Elemtype;
//树 (孩子兄弟表示法)
typedef struct CSNode{
Elemtype data;
struct CSNode *firstchild; //第一个孩子
struct CSNode *nextsibling; //下一个兄弟
}CSNode, *CSTree;
//创建 树
CSTree Create_CSTree(){
Elemtype data;
CSTree T;
scanf("%c", &data); //输入节点数据
getchar();
if(data == '#') //输入 - 以停止创建子树
return NULL;
T = (CSTree)malloc(sizeof(CSNode));
T->data = data;
printf("输入 %c 的第一个孩子数据(#停止): ", data); //递归创建
T->firstchild = Create_CSTree();
printf("输入 %c 的下一个兄弟数据(#停止): ", data);
T->nextsibling = Create_CSTree();
return T;
}
二叉树
二叉树是节点度数不大于 2 的有序树,是简单而又广泛运用的重要数据结构。
二叉树基本结构
//二叉树 结构体
typedef struct BiNode{
Elemtype data;
struct BiNode *leftchild; //左儿子
struct BiNode *rightchild; //右儿子
}BiNode, *BinaryTree;
森林
森林很好理解,就是多个树组成的集合。
森林基本结构
//森林 结构体
typedef struct {
CSTree ct[MAX];
int n; //树的个数
}forest, *Forest;
树、二叉树和森林的转换
树 转换为 二叉树
树 --> 二叉树步骤
(1)将树中每个相邻的兄弟进行连线;
(2)将每个节点除第一个孩子以外,断开其他兄弟与其双亲节点的连线;
(3)适当旋转处理完后的树,使其呈现二叉树的形式。
树 --> 二叉树图解
(1)加线、(2)断线
(3)适当旋转
由于我们使用的是孩子兄弟表示法,其实本质上用了类似二叉树的结构存储了树。所以树的 firstchild 对应了需要转换成的二叉树的 leftchild,而 nextsibling 对应了需要转换成的二叉树的 rightchild。我们只需要递归操作使其一一对应相等即可。
树 --> 二叉树代码
//树 转化为 二叉树
BinaryTree CSTree_Transform_to_BinaryTree(CSTree ct){
if(!ct) return NULL;
BinaryTree T = (BinaryTree)malloc(sizeof(BiNode));
T->data = ct->data;
//相当于将left变为firstchild, 将right变为nextsibling 本质的形态没有改变
T->leftchild = CSTree_Transform_to_BinaryTree(ct->firstchild);
T->rightchild = CSTree_Transform_to_BinaryTree(ct->nextsibling);
return T;
}
二叉树 转换为 树
二叉树 --> 树步骤
(1)先将二叉树的所有右孩子调整至同一层;
(2)若某个节点为双亲节点的左孩子,将该节点的沿右边的左右节点与其双亲节点连接;
(3)删除同一层所有横向的连线。
二叉树 --> 树图解
(1)调整至同一层
(2)加线、(3)断线
二叉树转换为树其实就是树转换为二叉树逆运算,本质上都是一致的,同样只需要递归操作使得 leftchild 等于 firstchild ,rightchild 等于 nextsibling 即可
二叉树 --> 树代码
//二叉树 转化 为树
CSTree BinaryTree_Transform_to_CSTree(BinaryTree bt){
if(!bt) return NULL;
CSTree T = (CSTree)malloc(sizeof(CSNode));
T->data = bt->data;
//相当于将firstchild变为left, 将nextsibling变为right 本质的形态没有改变
T->firstchild = BinaryTree_Transform_to_CSTree(bt->leftchild);
T->nextsibling = BinaryTree_Transform_to_CSTree(bt->rightchild);
return T;
}
森林 转换为 二叉树
森林 --> 二叉树步骤
(1)将森林的每个树转换为二叉树;
(2)将每个二叉树按照原顺序将其根节点通过右孩子依次连接,变成一整个二叉树。
森林 --> 二叉树图解
(1)每个树变成二叉树(2)按照顺序通过右孩子连接
森林用的是顺序的结构来存储多个树,因此我们只需要用 low 标记一下当前树的位置下标,当递归操作到达 high 时,就停止递归。
森林 --> 二叉树代码
//森林 转化为 二叉树
BinaryTree Forest_Transform_to_BinaryTree(CSTree ct[], int low, int high){
if(low > high) return NULL;
//每个树变成二叉树
BinaryTree T = CSTree_Transform_to_BinaryTree(ct[low]);
//通过rightchild连接每一个二叉树的根节点
T->rightchild = Forest_Transform_to_BinaryTree(ct, low + 1, high);
return T;
}
二叉树 转换为 森林
在上面森林转换为二叉树的过程中,我们可以发现,二叉树从根节点沿右下方的所有节点正好对应了森林每个树的根节点,我们从这一点出发,进行森林转换为二叉树的逆运算,就可以完成二叉树转换为森林的操作。
二叉树 --> 森林步骤
(1)断开二叉树根节点沿右下方孩子节点所有的连线,分成多个二叉树;
(2)将每个二叉树转换为树,形成森林。
二叉树 --> 森林图解
(1)断线
(2)每个二叉树转换为树
我们定义一个指针 p 从二叉树的根节点开始一直往右走,同时定义一个 q 来记录要断开的节点即 p->right ,依次将右孩子的连线断开,由此将所有二叉树转换为树。
二叉树 --> 森林代码
//二叉树 转化为 森林
Forest BinaryTree_Transform_to_Forest(BinaryTree bt){
Forest F = (Forest)malloc(sizeof(forest));
BinaryTree p = bt;
BinaryTree q = NULL;
int count = 0;
while(p){
q = p->rightchild; //q指向要切断连接的下一个节点(即其右儿子)
p->rightchild = NULL; //切断连接 形成单独的树
F->ct[count++] = BinaryTree_Transform_to_CSTree(p);//二叉树 转化为 树存到森林中
p = q; //p指向下一个节点 重复操作
}
F->n = count; //记录森林中 树的个数
return F;
}