数据结构通用树tree的实现-附带可用代码

本文通过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;
};

以下用用图说明通用树的管理模型:

在这里插入图片描述
可以看到最下面是一个链表,用于管理树的所有节点,链表节点存储树节点的地址。

三、树的基本操作

  1. 创建树
    Tree* Tree_Create();

  2. 销毁树
    void Tree_Destroy(Tree* tree);

  3. 清除树
    void Tree_Clear(Tree* tree);

  4. 插入节点
    int Tree_Insert(Tree* tree, TreeNode* node, int pPos);

  5. 删除节点
    TreeNode* Tree_Delete(Tree* tree, int Pos);

  6. 获取节点
    TreeNode* Tree_Get(Tree* tree, int Pos);

  7. 获取根节点
    TreeNode* Tree_Root(Tree* tree);

  8. 获取高度
    int Tree_Height(Tree* tree);

  9. 获取节点数
    int Tree_Count(Tree* tree);

  10. 获取度
    int Tree_Degree(Tree* tree);

  11. 显示树的结构
    void Tree_Display(Tree* tree, Print_Tree* print_tree);

接下来将介绍每一个操作的具体实现过程:

  1. 创建树
    Tree* Tree_Create();
Tree* Tree_Create()
{
	printf("create tree succeed\n");
	return LinkList_Create();
}

创建树很简单,直接创建一个链表作为一棵树,链表可以看上一篇文章:通用链表LinkList用例-可用demo

  1. 销毁树
    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;
}

销毁树首先清除树的所有节点,然后将创建的链表销毁

  1. 清除树
    void Tree_Clear(Tree* tree);
void Tree_Clear(Tree* tree)
{
	Tree_Delete(tree, 0);
	printf("clear tree succeed\n");
	return;
}

清除树我们调用树的删除,这里因为是清除整颗树,所以要删除根节点,删除根节点会将所有的节点都删除

  1. 插入节点
    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);	
			}
  1. 删除节点
    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);
  1. 获取节点
    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;
}

获取节点很简单,直接获取树链表中对应的节点

  1. 获取根节点
    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;
}

获取根节点直接获取第一个节点

  1. 获取高度
    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;
}
  1. 获取节点数
    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;	
}
  1. 显示树的结构
    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

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值