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