数据结构c语言——树的三种存储结构(双亲表示法、孩子表示法、兄弟表示法)

在大量的应用中,人们曾使用多种形式的存储结构来表示树。这里,我们介绍3种常用的链表结构。

1.双亲表示法:
假设以一组连续空间存储树的结点,同时在每个结点中附设一个指示器指示其双亲结点在链表中的位置,其形式说明如下:
在这里插入图片描述
在这里插入图片描述
例如,图6.13展示一棵树及其双亲表示的存储结构。

这种存储结构利用了每个结点(除根以外)只有惟一的双亲的性质。PARENT(T,x)操作可以在常量时间内实现。反复调用PARENT操作,直到遇见无双亲的结点时,便找到了树的根,这就是ROOT(x)操作的执行过程。但是,在这种表示法中,求结点的孩子时需要遍历整个结构

2.孩子表示法

由于树中每个结点可能有多棵子树,则可用多重链表,即每个结点有多个指针域,其中每个指针指向一棵子树的根结点,此时链表中的结点可以有如下两种结点格式。
在这里插入图片描述
若采用第一种结点格式,则多重链表中的结点是同构的,其中d为树的度。由于树中很多结点的度小于d,所以链表中有很多空链域,空间较浪费,不难推出,在一棵有n个结点度为k的树中必有n(k-1)+1个空链域。若采用第二种结点格式,则多重链表中的结点是不同构的,其中J为结点的度,degre域的值同J。此时,虽能节约存储空间,但操作不方便。

另一种办法是把每 个结点的孩子结点排列起来 ,看成是一个线性表,且以单链表作存储结构,则n个结点有n个孩子链表(叶子的孩子链表为空表)。而n个头指针又组成一个线性表,为了便于查找,可采用顺序存储结构。这种存储结构可形式地说明如下:

在这里插入图片描述
在这里插入图片描述
图6.14(a)是图6.13中的树的孩子表示法。与双亲表示法相反,孩子表示法便于那些涉及孩子的操作的实现,却不适用于PARENT(T,x)操作,我们可以把双亲表示法和孩子表示法结合起来,即将双亲表示和孩子链表合在一起。图6.14(b)就是这种存储结构的一例,它和图6.14(a)表示的是同一棵树。

3、孩子兄弟表示法: (相当于前面讲的将一颗树转换成一颗二叉树)
又称二叉树表示法,或二又链表表示法,即以二又链表作树的存储树的存储结构。链表中结点的两个链城分别指向该结点的第一个孩子结点和下一个兄弟结点,分别命名为firstchild城和nextsibling城。
在这里插入图片描述
在这里插入图片描述
图6.15是图6.13中的树的孩子兄弟链表。利用这种存储结构便于实现各种树的操作。首先易于实现找结点孩子等的操作。例如:若要访问结点x的第i个孩子,则只要先从firstchild域找到第1个孩子结点,然后沿着孩子结点的nextsibling域连续走i-1步,便可找到x的第i个孩子。当然,如果为每个结点增设一个PARENT域,则同样能方便地实现PARENT(T,x)操作。

  • 11
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用双亲-孩子链表表示法实现的各算法,和与二叉的转换算法的代码,包括主函数测试: ```c #include <stdio.h> #include <stdlib.h> #define MAX_TREE_SIZE 100 // 的最大结点数 #define MAX_QUEUE_SIZE 100 // 队列的最大容量 // 双亲-孩子链表结点结构体 typedef struct CTNode { int child; // 孩子结点的索引 struct CTNode *next; // 指向下一个孩子结点的指针 } CTNode, *ChildPtr; // 双亲-孩子链表头结点结构体 typedef struct { int data; // 结点数据 int parent; // 双亲结点的索引 ChildPtr firstChild; // 指向第一个孩子结点的指针 } CTBox; // 双亲-孩子链表结构体 typedef struct { CTBox nodes[MAX_TREE_SIZE]; // 结点数组 int r, n; // 根结点的索引和结点数 } CTree; // 初始化双亲-孩子链表 void InitTree(CTree *T) { int i; for (i = 0; i < MAX_TREE_SIZE; i++) { T->nodes[i].parent = -1; T->nodes[i].firstChild = NULL; } T->r = -1; T->n = 0; } // 向双亲-孩子链表中插入结点 void InsertNode(CTree *T, int i, int j) { ChildPtr p = (ChildPtr) malloc(sizeof(CTNode)); p->child = j; p->next = T->nodes[i].firstChild; T->nodes[i].firstChild = p; T->nodes[j].parent = i; T->n++; } // 遍历双亲-孩子链表 void TraverseTree(CTree *T) { int i, j; ChildPtr p; printf("Tree:\n"); for (i = 0; i < T->n; i++) { printf("%d: %d -> ", i, T->nodes[i].data); p = T->nodes[i].firstChild; while (p != NULL) { j = p->child; printf("%d -> ", T->nodes[j].data); p = p->next; } printf("NULL\n"); } } // 双亲-孩子链表转换为二叉 void CTreeToBiTree(CTree *T, int i, int *j, BiTree *p) { ChildPtr q; int k; q = T->nodes[i].firstChild; if (q == NULL) { *p = NULL; } else { CTreeToBiTree(T, q->child, &k, p); *j = k; while (q->next != NULL) { q = q->next; CTreeToBiTree(T, q->child, &k, &((*p)->rchild)); (*p)->rchild = (k == -1) ? NULL : (BiTree) k; } } } // 二叉转换为双亲-孩子链表 void BiTreeToCTree(BiTree T, int i, int *j, CTree *p) { if (T == NULL) { *j = -1; } else { p->nodes[i].data = T->data; p->nodes[i].parent = -1; (*j)++; BiTreeToCTree(T->lchild, *j, j, p); if (*j != -1) { InsertNode(p, i, *j); } BiTreeToCTree(T->rchild, *j, j, p); } } // 层序遍历二叉 void LevelOrderTraverse(BiTree T) { BiTree Q[MAX_QUEUE_SIZE]; int front = 0, rear = 0; if (T != NULL) { Q[++rear] = T; } while (front != rear) { BiTree p = Q[++front]; printf("%d ", p->data); if (p->lchild != NULL) { Q[++rear] = p->lchild; } if (p->rchild != NULL) { Q[++rear] = p->rchild; } } printf("\n"); } int main() { CTree T; BiTree B; int j = -1; InitTree(&T); T.r = 0; T.n = 10; T.nodes[0].data = 0; T.nodes[1].data = 1; T.nodes[2].data = 2; T.nodes[3].data = 3; T.nodes[4].data = 4; T.nodes[5].data = 5; T.nodes[6].data = 6; T.nodes[7].data = 7; T.nodes[8].data = 8; T.nodes[9].data = 9; InsertNode(&T, 0, 1); InsertNode(&T, 0, 2); InsertNode(&T, 1, 3); InsertNode(&T, 1, 4); InsertNode(&T, 2, 5); InsertNode(&T, 3, 6); InsertNode(&T, 3, 7); InsertNode(&T, 5, 8); InsertNode(&T, 5, 9); TraverseTree(&T); CTreeToBiTree(&T, 0, &j, &B); printf("Binary Tree:\n"); LevelOrderTraverse(B); BiTreeToCTree(B, 0, &j, &T); TraverseTree(&T); return 0; } ``` 在该代码中,我们首先定义了双亲-孩子链表结点结构体 CTNode 和双亲-孩子链表头结点结构体 CTBox,然后定义了双亲-孩子链表结构体 CTree。在 InitTree 函数中,我们将中所有结点的双亲结点索引设置为 -1,孩子结点指针设置为 NULL,根结点的索引设置为 -1,结点数设置为 0。在 InsertNode 函数中,我们向中插入一个孩子结点,并建立孩子结点与双亲结点之间的关系。在 TraverseTree 函数中,我们遍历整个,输出每个结点的数据以及它的孩子结点。在 CTreeToBiTree 函数中,我们将双亲-孩子链表转换为二叉,首先遍历的第一个孩子结点,得到它的索引 k,然后遍历它的兄弟结点,为每一个兄弟结点创建一个右孩子,并递归地将它的子转换为二叉。在 BiTreeToCTree 函数中,我们将二叉转换为双亲-孩子链表,首先为每个结点创建一个对应的双亲-孩子链表结点,并递归地将左子和右子插入到它们的双亲结点中。在 LevelOrderTraverse 函数中,我们使用队列实现层序遍历二叉。最后,在主函数中,我们创建了一个双亲-孩子链表,并向其中插入一些结点,然后遍历该,将它转换为二叉,输出二叉,并将二叉转换为双亲-孩子链表,输出双亲-孩子链表

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱睡觉的小馨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值