赞
踩
树是“一对多”的非线性存储结构,较链表、队列那些,树的概念会多点,涵盖的范围也较广。
双亲节点:以A、B、C、D为例,A是BCD的双亲节点;
孩子节点:以A、B、C、D为例,BCD是A的子节点;
兄弟节点:BCD有共同的父节点A,所以BCD之间又互为兄弟节点;
叶子节点:没有孩子节点的节点,例如K、L、M节点;
根节点:没有双亲结点的节点;
子树:A是整棵树的树根节点,单看BEFKL节点,也可以看成单独的树,B节点为该树的树根, 所以BEFKL节点称为子树;
树:由根节点和若干子树构成;单个节点也可以看成树,只有一个树根节点;
节点的度:该节点拥有孩子节点的个数,A节点的度为3;
树的度:比较树中各节点的度,最大节点的度为该树的度,以上的树的度为3;
树的深度(高度):根节点为第一层,然后依此类推,KLM在第四层,所以,以上树深度为4;
有序树:树中各节点位置不能互换的叫有序树;无序树,节点位置则可以互换;
空树:没有任何节点;
森林:由 n (n>=0)个互不相交的树组成的叫森林;以上去除 A 节点,分别以 B、C、D 为根节点的三棵子树就可以称为森林。
二叉树:顾名思义,就是子节点的个数不能超过2个的有序树,只能0、1和2;二叉树又可以细分为满二叉树和完全二叉树;
满二叉树:二叉树中除了叶子结点,每个结点的度都为 2,则称为满二叉树;
完全二叉树:一个深度为k,有n个节点的二叉树中,节点按从上至下、从左到右的顺序进行编号,编号为 i (1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同则为完全二叉树。
公式:先“根”节点,再左子树,后右子树。
此处说的“根”节点并不是特指树的根节点,如图2,由树的根节点A开始遍历,然后遍历左节点B,然后以B节点作为“根”节点,继续套用公式,再左后右,所以下一个遍历D节点,直到叶子节点处不再有子节点,就往回找右节点,以此类推......
图2的前序遍历最终顺序为:A->B->D->H->E->C->F->I->G。
公式:先左子树,再“根”节点,后右子树。
如图2,把该树看成由根节点A和左、右子树组成的一棵树;先看左子树:套公式,先左,从H节点开始,再“根”遍历D,该“根”没有右节点,则继续往上找(在D、H组成的子树中,D为根,H为左;而在B、D、E组成的子树中,B为根,D为左,E为右节点),接下来遍历“根”节点B,后遍历右节点E,以此类推......
图2的中序遍历最终顺序为:H->D->B->E->A->I->F->C->G。
公式:先左子树,再右子树,后根节点。
后序遍历就不多解释了,跟前两个差不多逻辑,
图2的后序遍历最终顺序为:H->D->E->B->I->F->G->C->A。
接下来我们要实现的树形结构并非二叉树,双亲节点下可以有多个孩子节点;
功能:
①可以在树形结构中,指定的节点下添加孩子节点;
②遍历树中节点,找出指定节点的双亲节点和孩子节点,并打印在终端;
③退出程序并释放节点内存空间。
节点数据最大不能超过32个字节;
struct _bro结构体,用来链接双亲节点下的各兄弟节点;
struct _node结构体中,data指向节点具体数据,parent指向该节点的双亲节点,head_child指向首个孩子节点,child_count表示该节点下的孩子节点个数,cur_high表示该节点所在树结构中的层数;
struct _tree结构体,包含一个节点变量和树的深度(tree_high)。
- typedef char Data_type; //数据类型
- #define DATA_LEN_MAX 32
-
- typedef struct _bro { //亲兄弟
- struct _bro *next;
- }Bro_t;
-
- typedef struct _node {
- Data_type *data; //节点数据
- struct _node *parent; //双亲节点
- struct _node *head_child; //孩子节点
- int child_count; //孩子个数
- Bro_t bro_list; //亲兄弟节点链表
- int cur_high; //当前节点层次,根节点为1
- }Node_t;
-
- typedef struct _tree {
- Node_t *node; //树的节点
- int tree_high; //树的高度
- }Tree_t;

传入参数:树和双亲结点数据;
通过比较节点数据找到双亲节点,并将该节点地址值返回;
查找的方法就是二叉树的先序遍历方法,先根节点,再左子树,后右子树,直到找到为止,在这里兄弟节点的链表就发挥了很大作用。
- /*****************************************
- * Fuction :查找双亲节点(类似前序遍历)
- * root :传递树根
- * Pdata :传递查找的父节点数据
- * ***************************************/
- Node_t *find_parent_node(Tree_t *root,Data_type *Pdata)
- {
- Node_t *p = root->node;
-
- while(1) {
- if(strcmp(p->data,Pdata) == 0){ //找到节点 退出
- //printf("find parent node succeed .\n");
- return p;
- }
- else{
- if(p->head_child != NULL) { //判断该节点是否有子节点
- p = p->head_child;
- }
- else {
- if(p->bro_list.next != NULL) { //没有子节点,则判断是否有兄弟节点
- p = (Node_t *)p->bro_list.next;
- }
- else {
- while(1) {
- p = p->parent;
- if(p->parent == NULL) { //往树根节点方向查找还未遍历的节点,直到树根处退出遍历
- printf("find parent node fail !\n");
- return NULL;
- }
- if(p->bro_list.next != NULL) {
- p = (Node_t *)p->bro_list.next;
- break;
- }
- }
- }
- }
- }
- }
- }

参数:传递新添加的节点;
双亲节点的head_child作为兄弟链表表头,表尾node->bro_list.next指向NULL表示结束。
- /********************************
- * Fuction :链接亲兄弟节点
- * node :添加的节点
- * ******************************/
- void bro_node_list(Node_t *node)
- {
- Node_t *p = node->parent;
- Node_t *q = p->head_child;
-
- if(p->child_count == 0) {
- p->head_child = node;
- node->bro_list.next = NULL;
- p->child_count += 1;
- }
- else {
- while(q->bro_list.next != NULL){
- q = (Node_t *)q->bro_list.next;
- }
- q->bro_list.next = (Bro_t *)node;
- node->bro_list.next = NULL;
- }
- }

这里需要传递三个参数:树,父节点数据,新节点数据;
首先,需要为所创建的节点申请结构体变量内存空间和节点数据存放空间,内存申请失败则直接退出创建;如果创建新节点之前为空树,则新创建的节点作为树根节点,否则就通过函数find_parent_node查找父节点的位置,查找失败退出,成功就添加新节点,并将新节点添加到兄弟链表中。
该函数主要做的就是填充结构体变量,使各节点联系起来。
- /*************************************************
- * Fuciton :在树中创建一个新节点,新节点挂在双亲节点下
- * root :传递树根节点
- * Pdata :双亲节点的数据
- * Data :新节点的数据
- * ***********************************************/
- void create_tree_node(Tree_t *root, Data_type *Pdata, Data_type *Data)
- {
- Node_t *node = (Node_t *)malloc(sizeof(Node_t));
- Node_t *p = NULL;
- Data_type *data_buf = (Data_type *)malloc(strlen(Data)+ 1);
-
- if((node == NULL) || (data_buf == NULL)) {
- printf("malloc failed !\n");
- return ;
- }
-
- strcpy(data_buf,Data);
-
- if(root->node == NULL) { //树根
- root->node = node;
- root->tree_high = 1;
- node->data = data_buf;
- node->parent = NULL;
- node->head_child = NULL;
- node->child_count = 0;
- node->bro_list.next = NULL;
- node->cur_high = 1;
- }
- else {
- p = find_parent_node(root,Pdata);
- if(p != NULL) { //找到双亲节点
- node->data = data_buf;
- node->parent = p;
- node->head_child = NULL;
- node->child_count = 0;
- node->cur_high = p->cur_high + 1;
- if(node->cur_high > root->tree_high) {
- root->tree_high = node->cur_high;
- }
- bro_node_list(node);
- }
- else {
- free(node);
- free(data_buf);
- }
- }
- }

参数:传递树;
节点的内存释放只能从叶子节点开始,不然各节点的联系就会被破坏,从而无法找到其它节点,这里利用后序遍历法进行查找,先左子树,再右子树,后根节点;
通过for(p; p->head_child != NULL; p = p->head_child)查找树的最终左子树,然后判断该节点在兄弟链表中是否有其它的节点,如果有,则去查找该子树的叶子节点,从叶子节点开始释放,没有的话就释放掉,回到双亲节点继续查找。
- /**********************************
- * Fuction :释放掉节点申请的内存(后序遍历方法)
- * root :传递树根节点
- * *******************************/
- void free_tree_nodes(Tree_t *root)
- {
- Node_t *p = root->node;
- Node_t *temp = NULL;
- int free_count = 0;
-
- if(p == NULL) {
- printf("empty tree! \n");
- return;
- }
- for(p; p->head_child != NULL; p = p->head_child); //查找左子树的叶子节点
-
- do {
- if(p->bro_list.next != NULL) { //判断该节点是否有兄弟节点
- temp = p;
- printf(" free data is %s\n",temp->data);
- p = (Node_t *)p->bro_list.next;
- free(temp);
- for(p; p->head_child != NULL; p = p->head_child);
- }
- else {
- temp = p;
- printf(" free data is %s\n",temp->data);
- p = p->parent;
- free(temp);
- }
- ++free_count;
- }while(p != NULL);
- printf("%d-Node memory free OK.\n",free_count);
- }

- /****************************************
- Fuction:C语言实现树
- Time:11/21/2022
- Author:Qurry
- ****************************************/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- typedef char Data_type; //数据类型
- #define DATA_LEN_MAX 32
-
- typedef struct _bro { //亲兄弟
- struct _bro *next;
- }Bro_t;
-
- typedef struct _node {
- Data_type *data; //节点数据
- struct _node *parent; //双亲节点
- struct _node *head_child; //孩子节点
- int child_count; //孩子个数
- Bro_t bro_list; //亲兄弟节点链表
- int cur_high; //当前节点层次,根节点为1
- }Node_t;
-
- typedef struct _tree {
- Node_t *node; //树的节点
- int tree_high; //树的高度
- }Tree_t;
-
- /*****************************************
- * Fuction :查找双亲节点(类似前序遍历)
- * root :传递树根
- * Pdata :传递查找的父节点数据
- * ***************************************/
- Node_t *find_parent_node(Tree_t *root,Data_type *Pdata)
- {
- Node_t *p = root->node;
-
- while(1) {
- if(strcmp(p->data,Pdata) == 0){ //找到节点 退出
- //printf("find parent node succeed .\n");
- return p;
- }
- else{
- if(p->head_child != NULL) { //判断该节点是否有子节点
- p = p->head_child;
- }
- else {
- if(p->bro_list.next != NULL) { //没有子节点,则判断是否有兄弟节点
- p = (Node_t *)p->bro_list.next;
- }
- else {
- while(1) {
- p = p->parent;
- if(p->parent == NULL) { //往树根节点方向查找还未遍历的节点,直到树根处退出遍历
- printf("find parent node fail !\n");
- return NULL;
- }
- if(p->bro_list.next != NULL) {
- p = (Node_t *)p->bro_list.next;
- break;
- }
- }
- }
- }
- }
- }
- }
-
- /********************************
- * Fuction :链接亲兄弟节点
- * node :添加的节点
- * ******************************/
- void bro_node_list(Node_t *node)
- {
- Node_t *p = node->parent;
- Node_t *q = p->head_child;
-
- if(p->child_count == 0) {
- p->head_child = node;
- node->bro_list.next = NULL;
- p->child_count += 1;
- }
- else {
- while(q->bro_list.next != NULL){
- q = (Node_t *)q->bro_list.next;
- }
- q->bro_list.next = (Bro_t *)node;
- node->bro_list.next = NULL;
- }
- }
-
- /*************************************************
- * Fuciton :在树中创建一个新节点,新节点挂在双亲节点下
- * root :传递树根节点
- * Pdata :双亲节点的数据
- * Data :新节点的数据
- * ***********************************************/
- void create_tree_node(Tree_t *root, Data_type *Pdata, Data_type *Data)
- {
- Node_t *node = (Node_t *)malloc(sizeof(Node_t));
- Node_t *p = NULL;
- Data_type *data_buf = (Data_type *)malloc(strlen(Data)+ 1);
-
- if((node == NULL) || (data_buf == NULL)) {
- printf("malloc failed !\n");
- return ;
- }
-
- strcpy(data_buf,Data);
-
- if(root->node == NULL) { //树根
- root->node = node;
- root->tree_high = 1;
- node->data = data_buf;
- node->parent = NULL;
- node->head_child = NULL;
- node->child_count = 0;
- node->bro_list.next = NULL;
- node->cur_high = 1;
- }
- else {
- p = find_parent_node(root,Pdata);
- if(p != NULL) { //找到双亲节点
- node->data = data_buf;
- node->parent = p;
- node->head_child = NULL;
- node->child_count = 0;
- node->cur_high = p->cur_high + 1;
- if(node->cur_high > root->tree_high) {
- root->tree_high = node->cur_high;
- }
- bro_node_list(node);
- }
- else {
- free(node);
- free(data_buf);
- }
- }
- }
-
- /**********************************
- * Fuction :添加多个子树节点
- * root :传递树根节点
- * *******************************/
- void add_tree_nodes(Tree_t *root)
- {
- Data_type pdata_buf[DATA_LEN_MAX],data_buf[DATA_LEN_MAX];
- memset(pdata_buf,0,DATA_LEN_MAX);
- memset(data_buf,0,DATA_LEN_MAX);
- printf("window下输入ctrl+z完成创建;linux下输入ctrl+d完成创建\n");
- if(root->node == NULL){
- printf("please input tree root data : \n");
- scanf("%s",data_buf);
- create_tree_node(root, NULL, data_buf);
- }
- printf("please input Pdata & data :\n");
- while(scanf("%s %s",pdata_buf,data_buf) != EOF) {
- printf("pdata_buf:%s,data_buf:%s \n",pdata_buf,data_buf);
- create_tree_node(root, pdata_buf, data_buf);
- }
- printf("tree high is %d\n",root->tree_high);
- }
-
- /**********************************
- * Fuction :遍历打印树形结构
- * root :传递树根节点
- * *******************************/
- void print_tree_node(Tree_t *root)
- {
- Data_type temp[DATA_LEN_MAX];
- Node_t *p = NULL;
- int i = 0;
- printf("please input node data :");
- scanf("%s",temp);
- p = find_parent_node(root,temp);
- if(p == NULL) {
- printf("node data error! \n");
- }
- else {
- if(p->parent != NULL)
- printf("Node parent data :%s\n",p->parent->data);
- if(p->head_child != NULL) {
- p = p->head_child;
- while(1) {
- printf("Node child-%d:%s\n",i++,p->data);
- if(p->bro_list.next == NULL)
- break;
- p = (Node_t *)p->bro_list.next;
- }
- }
- }
- }
-
- /**********************************
- * Fuction :释放掉节点申请的内存(后序遍历方法)
- * root :传递树根节点
- * *******************************/
- void free_tree_nodes(Tree_t *root)
- {
- Node_t *p = root->node;
- Node_t *temp = NULL;
- int free_count = 0;
-
- if(p == NULL) {
- printf("empty tree! \n");
- return;
- }
- for(p; p->head_child != NULL; p = p->head_child); //查找左子树的叶子节点
-
- do {
- if(p->bro_list.next != NULL) { //判断该节点是否有兄弟节点
- temp = p;
- printf(" free data is %s\n",temp->data);
- p = (Node_t *)p->bro_list.next;
- free(temp);
- for(p; p->head_child != NULL; p = p->head_child);
- }
- else {
- temp = p;
- printf(" free data is %s\n",temp->data);
- p = p->parent;
- free(temp);
- }
- ++free_count;
- }while(p != NULL);
- printf("%d-Node memory free OK.\n",free_count);
- }
-
- /*****************************
- * Fuction : 模式菜单
- * **************************/
- int menu_select()
- {
- char mode[10];
- printf("1:添加 2:打印 0:退出\n");
- printf("Please input mode num: ");
- scanf("%s",&mode);
- if(mode[0] == '1')
- return 1;
- else if(mode[0] == '2')
- return 2;
- else if(mode[0] == '0')
- return 0;
- else
- return -1;
- }
-
- void main()
- {
- int mode = -1;
- Tree_t tree_root = {
- .node = NULL,
- .tree_high = 0,
- };
- Tree_t *tree_p = &tree_root;
-
- while(1) {
- mode = menu_select();
- switch(mode)
- {
- case 0:
- free_tree_nodes(tree_p);
- return ;
-
- case 1:
- add_tree_nodes(tree_p);
- break;
-
- case 2:
- print_tree_node(tree_p);
- break;
-
- default :
- printf("mode input error !\n");
- break;
- }
- }
- }

按照上图 图1 的树形结构进行测试,结果如下:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。