数组描述子节点的缺点
如果有这么一颗奇葩的树,大多数节点的孩子数为1-2个,但是有一个节点的孩子数是100个。
因为我们使用数组描述子节点,所以描述子节点的数组得定义为struct TreeNode* children[100];
。
也就是说,除了有一个充分利用了数组分配的空间,其他的都造成了极大浪费,毫无疑问不合理。
使用链表描述
孩子也是一个一维的集合,特点个数不确定,符合这种特点的数据结构就是链式线性表了(简称链表)。
我们再来仔细观察下树的结构图,来尝试定义节点的数据结构:
- 首先一个节点得有一个数据区域,保存节点的数据信息,最简单就是保存一个int数据。
- 每个节点有多个孩子,这个可以使用一个链表来描述,因为链表指定了头指针,就能把整个链表带出来,所以可以定义节点为。
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;
}
执行结果
毫无疑问,执行结果验证了代码的真实性,但是输出顺序稍微有点乱。
主要是因为现在的输出是顺着一个节点,将其子节点输出完,才输出另外节点的逻辑,如果不好理解下个断点走走就是了。