本文通过C语言实现通用的树,可以作为树的模板使用
一、树的基本知识点
二、通用树实现的原理
三、树的基本操作
四、测试用例
一、树的基本知识点
这里省略
二、通用树实现的原理
树的每一个节点包含3部分组成,第一部分是用于存储调用者传的数据,用void类型指向所在地址,第二部分是一个指向双亲节点的指针,类型是TTreeNode,第三部分是一个链表,用于管理该节点所包含的子节点。如下是对树节点的定义:
typedef void Tree;
typedef void TreeNode;
typedef void TreeData;
typedef struct _tagTreeNode TTreeNode;
struct _tagTreeNode
{
TreeData* data;
TTreeNode* parent;
LinkList* child;
};
以下用用图说明通用树的管理模型:
可以看到最下面是一个链表,用于管理树的所有节点,链表节点存储树节点的地址。
三、树的基本操作
-
创建树
Tree* Tree_Create(); -
销毁树
void Tree_Destroy(Tree* tree); -
清除树
void Tree_Clear(Tree* tree); -
插入节点
int Tree_Insert(Tree* tree, TreeNode* node, int pPos); -
删除节点
TreeNode* Tree_Delete(Tree* tree, int Pos); -
获取节点
TreeNode* Tree_Get(Tree* tree, int Pos); -
获取根节点
TreeNode* Tree_Root(Tree* tree); -
获取高度
int Tree_Height(Tree* tree); -
获取节点数
int Tree_Count(Tree* tree); -
获取度
int Tree_Degree(Tree* tree); -
显示树的结构
void Tree_Display(Tree* tree, Print_Tree* print_tree);
接下来将介绍每一个操作的具体实现过程:
- 创建树
Tree* Tree_Create();
Tree* Tree_Create()
{
printf("create tree succeed\n");
return LinkList_Create();
}
创建树很简单,直接创建一个链表作为一棵树,链表可以看上一篇文章:通用链表LinkList用例-可用demo
- 销毁树
void Tree_Destroy(Tree* tree);
void Tree_Destroy(Tree* tree)
{
if(tree != NULL)
{
Tree_Clear(tree);
LinkLisk_Destroy(tree);
printf("destroy tree succeed\n");
}
return;
}
销毁树首先清除树的所有节点,然后将创建的链表销毁
- 清除树
void Tree_Clear(Tree* tree);
void Tree_Clear(Tree* tree)
{
Tree_Delete(tree, 0);
printf("clear tree succeed\n");
return;
}
清除树我们调用树的删除,这里因为是清除整颗树,所以要删除根节点,删除根节点会将所有的节点都删除
- 插入节点
int Tree_Insert(Tree* tree, TreeNode* node, int pPos);
int Tree_Insert(Tree* tree, TreeNode* node, int pPos)
{
TTreeNode* mnode = NULL;
int ret = -1;
if(tree != NULL && node != NULL && pPos < LinkList_Length(tree))
{
mnode = (TTreeNode*)malloc(sizeof(TTreeNode));
if(mnode != NULL)
{
mnode->data = node;
mnode->parent = (TTreeNode*)LinkList_Get(tree, pPos);
mnode->child = LinkList_Create();
LinkList_Insert(tree, LinkList_Length(tree), (LinkListNode*)mnode);
if(mnode->parent != NULL)
{
LinkList_Insert(mnode->parent->child, LinkList_Length(mnode->parent->child), (LinkListNode*)mnode);
}
ret = 0;
}
else
{
free(mnode);
}
}
return ret;
}
插入节点首先将传入的节点赋值给data,然后获取节点的双亲节点,创建该节点的子节点链表,然后将节点插入树的链表中:
mnode->data = node;
mnode->parent = (TTreeNode*)LinkList_Get(tree, pPos);
mnode->child = LinkList_Create();
如果该节点有双亲,还要将其插入双亲节点的子节点链表中:
if(mnode->parent != NULL)
{
LinkList_Insert(mnode->parent->child, LinkList_Length(mnode->parent->child), (LinkListNode*)mnode);
}
- 删除节点
TreeNode* Tree_Delete(Tree* tree, int Pos);
TreeNode* Tree_Delete(Tree* tree, int Pos)
{
TTreeNode* mnode = NULL;
TreeNode* ret = NULL;
mnode = (TTreeNode*)LinkList_Get(tree, Pos);
if(mnode != NULL)
{
ret = mnode->data;
recursive_Delete(tree, mnode);
}
return ret;
}
因为删除节点的同时还要删除其子节点,所以这里用到了递归的思想,需要实现递归删除节点的函数:
void recursive_Delete(Tree* tree, TTreeNode* node)
{
int i = 0;
if(node != NULL && tree != NULL)
{
for(i=0; i<LinkList_Length(tree); i++)
{
TTreeNode* LinkListTreeNode = (TTreeNode*)LinkList_Get(tree, i);
if(LinkListTreeNode == node)
{
LinkList_Delete(tree, i);
break;
}
}
if(node->parent != NULL)
{
for(i=0; i<LinkList_Length(node->parent->child); i++)
{
TTreeNode* PTreeNode = (TTreeNode*)LinkList_Get(node->parent->child, i);
if(PTreeNode == node)
{
LinkList_Delete(node->parent->child, i);
break;
}
}
}
if(node->child != NULL)
{
while(LinkList_Length(node->child) > 0)
{
TTreeNode* CTreeNode = (TTreeNode*)LinkList_Get(node->child, 0);
recursive_Delete(tree, CTreeNode);
}
}
LinkList_Clear(node->child);
LinkLisk_Destroy(node->child);
}
}
递归删除节点分为4步:
(1)删除树链表中对应的节点:
for(i=0; i<LinkList_Length(tree); i++)
{
TTreeNode* LinkListTreeNode = (TTreeNode*)LinkList_Get(tree, i);
if(LinkListTreeNode == node)
{
LinkList_Delete(tree, i);
break;
}
}
(2)如果该节点有双亲节点,删除双亲节点中子节点链表对应的该节点:
if(node->parent != NULL)
{
for(i=0; i<LinkList_Length(node->parent->child); i++)
{
TTreeNode* PTreeNode = (TTreeNode*)LinkList_Get(node->parent->child, i);
if(PTreeNode == node)
{
LinkList_Delete(node->parent->child, i);
break;
}
}
}
(3)如果该节点有子节点,需要删除所有子节点:
if(node->child != NULL)
{
while(LinkList_Length(node->child) > 0)
{
TTreeNode* CTreeNode = (TTreeNode*)LinkList_Get(node->child, 0);
recursive_Delete(tree, CTreeNode);
}
}
(4)清除销毁节点的子节点链表
LinkList_Clear(node->child);
LinkLisk_Destroy(node->child);
- 获取节点
TreeNode* Tree_Get(Tree* tree, int Pos);
TreeNode* Tree_Get(Tree* tree, int Pos)
{
TTreeNode* mnode = NULL;
TreeNode* ret = NULL;
mnode = (TTreeNode*)LinkList_Get(tree, Pos);
if(mnode != NULL)
ret = mnode->data;
return ret;
}
获取节点很简单,直接获取树链表中对应的节点
- 获取根节点
TreeNode* Tree_Root(Tree* tree);
TreeNode* Tree_Root(Tree* tree)
{
TTreeNode* mnode = NULL;
TreeNode* ret = NULL;
mnode = (TTreeNode*)LinkList_Get(tree, 0);
if(mnode != NULL)
ret = mnode->data;
return ret;
}
获取根节点直接获取第一个节点
- 获取高度
int Tree_Height(Tree* tree);
int Tree_Height(Tree* tree)
{
TTreeNode* mnode = NULL;
int ret = 0;
mnode = (TTreeNode*)LinkList_Get(tree, 0);
if(mnode != NULL)
ret = recursive_height(mnode);
return ret;
}
这里也采用递归的思想,树的高度等于子节点最大高度+1:
int recursive_height(TTreeNode* node)
{
int i = 0;
int subNodeheight = 0;
int ret = 0;
if(node != NULL)
{
for(i=0; i<LinkList_Length(node->child); i++)
{
TTreeNode* CTreeNode = (TTreeNode*)LinkList_Get(node->child, i);
subNodeheight = recursive_height(CTreeNode);
if(ret < subNodeheight)
ret = subNodeheight;
}
ret = ret + 1;
}
return ret;
}
- 获取节点数
int Tree_Count(Tree* tree);
int Tree_Count(Tree* tree)
{
return LinkList_Length(tree);
}
节点数直接返回链表的长度
10. 获取度
int Tree_Degree(Tree* tree);
int Tree_Degree(Tree* tree)
{
TTreeNode* mnode = NULL;
int ret = 0;
mnode = (TTreeNode*)LinkList_Get(tree, 0);
if(mnode != NULL)
ret = recursive_degree(mnode);
return ret;
}
同样的道理,树的度也需要采用递归的思想,获取每一个节点的度并取最大的那个:
int recursive_degree(TTreeNode* node)
{
int i = 0;
int subNodeDegree = 0;
int ret = 0;
if(node != NULL)
{
for(i=0; i<LinkList_Length(node->child); i++)
{
TTreeNode* CTreeNode = (TTreeNode*)LinkList_Get(node->child, i);
subNodeDegree = recursive_degree(CTreeNode);
if(ret < subNodeDegree)
ret = subNodeDegree;
}
ret = LinkList_Length(node->child);
}
return ret;
}
- 显示树的结构
void Tree_Display(Tree* tree, Print_Tree* print_tree);
void Tree_Display(Tree* tree, Print_Tree* print_tree)
{
TTreeNode* mnode = NULL;
mnode = (TTreeNode*)LinkList_Get(tree, 0);
if(mnode != NULL)
recursive_dlsplay(mnode, print_tree, 0);
}
同样道理,树的显示也是采用递归思想,首先打印本节点,然后打印其子节点:
void recursive_dlsplay(TTreeNode* node, Print_Tree* print_tree, int count)
{
int i = 0;
if(node != NULL)
{
for(i=0; i<count; i++)
printf("##");
print_tree(node->data);
printf("\n");
for(i=0; i<LinkList_Length(node->child); i++)
{
recursive_dlsplay((TTreeNode*)LinkList_Get(node->child, i), print_tree, count+2);
}
}
return;
}
四、测试用例
#include<stdio.h>
#include "tree.h"
void tree_display(TreeData* data)
{
printf("%c", data);
}
int main()
{
Tree* tree = NULL;
tree = Tree_Create();
if(tree != NULL)
{
Tree_Insert(tree, (TreeNode*)'A', -1);
Tree_Insert(tree, (TreeNode*)'B', 0);
Tree_Insert(tree, (TreeNode*)'C', 0);
Tree_Insert(tree, (TreeNode*)'D', 0);
Tree_Insert(tree, (TreeNode*)'E', 1);
Tree_Insert(tree, (TreeNode*)'F', 1);
Tree_Insert(tree, (TreeNode*)'H', 3);
Tree_Insert(tree, (TreeNode*)'I', 3);
Tree_Insert(tree, (TreeNode*)'J', 3);
printf("full tree: \n");
Tree_Display(tree, tree_display);
printf("height: %d\n", Tree_Height(tree));
printf("degree: %d\n", Tree_Degree(tree));
printf("count: %d\n", Tree_Count(tree));
printf("delete node 1: \n");
Tree_Delete(tree, 1);
Tree_Display(tree, tree_display);
Tree_Destroy(tree);
}
return -1;
}
对应的makefile:
CC = g++
CFLAGS = -g -Wall -O
main:main.o LinkList.o tree.o
$(CC) $^ -o $@
%.o:%.c
$(CC) $(CFLAGS) -c $^
clean:
rm -rf main.o tree.o LinkList.o main
最后效果:
可以看到根节点是A,A的子节点有BCD,B的子节点有EF,D的子节点有HIJ,和预想的一样,到这里我们完成了通用树的所有操作。所有代码下载见:https://download.csdn.net/download/yjlyyj/85525739