继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

C语言数据结构(11)--使用链表描述子节点的普通树实现

程序员大阳
关注TA
已关注
手记 357
粉丝 1.5万
获赞 1523

数组描述子节点的缺点

如果有这么一颗奇葩的树,大多数节点的孩子数为1-2个,但是有一个节点的孩子数是100个。

因为我们使用数组描述子节点,所以描述子节点的数组得定义为struct TreeNode* children[100];

也就是说,除了有一个充分利用了数组分配的空间,其他的都造成了极大浪费,毫无疑问不合理。

使用链表描述

孩子也是一个一维的集合,特点个数不确定,符合这种特点的数据结构就是链式线性表了(简称链表)。

我们再来仔细观察下树的结构图,来尝试定义节点的数据结构:

图片描述

  1. 首先一个节点得有一个数据区域,保存节点的数据信息,最简单就是保存一个int数据。
  2. 每个节点有多个孩子,这个可以使用一个链表来描述,因为链表指定了头指针,就能把整个链表带出来,所以可以定义节点为。
typedef struct {
	int data;//数据区域
	TreeNode* node;//保存孩子中的头结点
}TreeNode;

上面的节点数据结构乍一看没啥问题,对于节点1来说,data区域保存1,node的data保存2,node的node指向3。

但是2下面的5和6呢,已经没有地方存储了。

我们再次仔细分析下节点,实际上每个节点除了要通过一个链表保存兄弟节点,还需要一个链表保存孩子节点。

于是结构应该为:

typedef struct {
	int data;//数据区域
	TreeNode* brother;//保存兄弟节点 如果为NULL表示无
	TreeNode* son;//保存孩子节点 如果为NULL表示无
}TreeNode;

也就是说,我们的存储结构实际上是,同样能表达含义一模一样的树。

图片描述

代码实现

嗯,这个代码已经有一定理解难度了,我也是写了大概几十分钟才搞定的,C语言真是魅力无穷吭,喷香!

/*
* 使用链表描述子节点的普通树实现
* 作者:熊猫大大
* 时间:2019-10-16
*/
#include <stdio.h>

typedef struct {
	int data;//数据区域
	struct TreeNode* brother;//保存兄弟节点 如果为NULL表示无
	struct TreeNode* son;//保存孩子节点 如果为NULL表示无
}TreeNode;

//为树的当前节点添加子节点
int addChild(TreeNode* curNode, int data)
{
	//分配新节点
	TreeNode* newNode= (TreeNode*)malloc(sizeof(TreeNode));
	newNode->data = data;
	newNode->brother = NULL;
	newNode->son = NULL;
	//找到当前节点的最后一个字节点
	TreeNode* lastSonNode = curNode->son;//指向第一个子节点
	if (lastSonNode == NULL) //第一个子节点即为空
	{
		curNode->son = newNode;
		return 1;
	}
	while (lastSonNode->brother != NULL) //找到最后一个节点
	{
		lastSonNode = lastSonNode->brother;
	}
	lastSonNode->brother = newNode;//将新节点挂在最后一个节点后面
	return 1;
}

//遍历当前节点与子节点
void printTree(TreeNode *node)
{
	printf("父节点:%d",node->data);
	//遍历打印子节点
	printf("----子节点:");
	TreeNode *p = node->son;
	if (p == NULL) //无子节点
	{
		printf("无\n");
		return 1;
	}
	if (p != NULL) 
	{
		printf("%d ",p->data);
	}
	while(p->brother!=NULL)
	{
		p = p->brother;
		printf("%d ", p->data);
	}
	printf("\n");
	//遍历递归子节点
	TreeNode *q = node->son;
	if (q != NULL)
	{
		printTree(q);
	}
	while (q->brother != NULL)
	{
		q = q->brother;
		printTree(q);
	}
}
//----------------------------------------------------------------------------------------------------测试入口区域
int main()
{
	//设定根节点
	TreeNode root;
	root.data = 1;//根节点数据区域
	root.brother = NULL;//根节点无兄弟节点
	root.son = NULL;//一开始也没有子节点
	//为1节点添加2/3/4子节点
	addChild(&root,2);
	addChild(&root, 3);
	addChild(&root, 4);
	//为2号节点添加5/6子节点
	TreeNode *node2 = root.son;
	addChild(node2, 5);
	addChild(node2, 6);
	//为3号节点添加7子节点
	TreeNode *node3 = node2->brother;
	addChild(node3, 7);
	//为4号节点添加8子节点
	TreeNode *node4 = node3->brother;
	addChild(node4, 8);
	printTree(&root);

	return 1;
}

执行结果

毫无疑问,执行结果验证了代码的真实性,但是输出顺序稍微有点乱。

主要是因为现在的输出是顺着一个节点,将其子节点输出完,才输出另外节点的逻辑,如果不好理解下个断点走走就是了。

图片描述

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP