这段代码的核心思想是使用链表来表示多叉树中每个节点的子节点关系,从而构建出一棵灵活的树结构。
为了帮助你理解,我将分为以下几个部分进行解释:
- 数据结构的设计
- 核心函数的功能和实现
- 举例分析运行流程
1️⃣ 数据结构的设计
这段代码中的树的设计如下:
-
TreeNode(树的节点):表示树的一个节点,包含两个信息:
data
:节点的数据,类型为eleType
,这里用char
表示树节点存储的字符数据。childrenHead
:一个链表指针,表示所有子节点的链表头,每个子节点通过一个ListNode
节点连接到这棵树上。
-
ListNode(链表的节点):用于表示一个子节点,每个
ListNode
中存储:data
:指向子节点的树节点的指针(TreeNode*
)。next
:指向下一个子节点,从而将多个子节点串联成一个单链表。
-
Tree(树):表示一棵完整的树,包含:
nodes
:用来存储树的所有节点,类型是TreeNode*
。root
:指向树的根节点,是树的入口。
2️⃣ 核心函数的功能和实现
1. void Add_TreeChild(TreeNode* parent, TreeNode* child)
这个函数的功能是将
child
作为parent
的子节点,并将其加入到parent
的子节点链表中。
实现思路:
- 创建一个
ListNode
,将child
节点的指针存储在ListNode
中。 - 如果
parent
没有任何子节点,则将child
作为链表的第一个节点(头节点)。 - 如果
parent
已有子节点,则使用头插法,将child
插入链表的最前面。
void Add_TreeChild(TreeNode* parent, TreeNode* child)
{
ListNode* childNode = (ListNode*)malloc(sizeof(ListNode)); // 创建子节点链表
childNode->data = child; // 指向子节点的树节点
childNode->next = parent->childrenHead; // 头插法
parent->childrenHead = childNode; // 更新头指针
}
2. void Creat_Tree(Tree* t, int size)
这个函数的功能是创建一棵有 size 个节点的树,并为每个节点分配内存。
void Creat_Tree(Tree* t, int size)
{
t->nodes = (TreeNode*)malloc(sizeof(TreeNode) * size); // 申请 size 个 TreeNode
t->root = NULL; // 暂时不设置根节点
}
3. void SetRoot_Tree(Tree* t, int id)
这个函数将
id
号节点设置为树的根节点。
void SetRoot_Tree(Tree* t, int id)
{
t->root = &t->nodes[id]; // 获取 id 号节点并设为根节点
}
4. void AssighData_Tree(Tree* t, int id, eleType data)
将树的第
id
号节点的data
设置为data
,并初始化其childrenHead
。
void AssighData_Tree(Tree* t, int id, eleType data)
{
t->nodes[id].data = data; // 给 id 号节点设置数据
t->nodes[id].childrenHead = NULL; // 初始化子链表为空
}
5. void SetLine_Tree(Tree* t, int parentId, int childId)
将
parentId
节点和childId
节点之间建立父子关系。
void SetLine_Tree(Tree* t, int parentId, int childId)
{
TreeNode* parentNode = &t->nodes[parentId]; // 获取父节点
TreeNode* childNode = &t->nodes[childId]; // 获取子节点
Add_TreeChild(parentNode, childNode); // 调用上面的 Add_TreeChild 函数
}
6. void Print_Tree(Tree* t, TreeNode* node)
采用先序遍历打印树的内容。
void Print_Tree(Tree* t, TreeNode* node)
{
if (node == NULL) node = t->root; // 如果 node 为空,则从根节点开始
printf("%c", node->data); // 打印当前节点的数据
ListNode* temp = node->childrenHead; // 遍历当前节点的子节点链表
while (temp)
{
Print_Tree(t, temp->data); // 递归打印子节点
temp = temp->next; // 继续下一个子节点
}
}
3️⃣ 运行流程举例
1. 构建一棵树
a
/ \
b c
/ / \
d e f
操作代码:
SetRoot_Tree(&T, 0); // 根节点为 a
AssighData_Tree(&T, 0, 'a');
AssighData_Tree(&T, 1, 'b');
AssighData_Tree(&T, 2, 'c');
AssighData_Tree(&T, 3, 'd');
AssighData_Tree(&T, 4, 'e');
AssighData_Tree(&T, 5, 'f');
SetLine_Tree(&T, 0, 1); // a -> b
SetLine_Tree(&T, 0, 2); // a -> c
SetLine_Tree(&T, 1, 3); // b -> d
SetLine_Tree(&T, 2, 4); // c -> e
SetLine_Tree(&T, 2, 5); // c -> f
结构分析
a
有 2 个子节点:b
和c
。b
有 1 个子节点:d
。c
有 2 个子节点:e
和f
。
2. 先序遍历的过程
打印顺序为:
a b d c e f
4️⃣ 小结
-
树的父子关系
- 使用链表
ListNode
来存储每个节点的子节点列表,每个TreeNode
的childrenHead
维护一个子节点的链表。
- 使用链表
-
函数的操作
Add_TreeChild
:将子节点添加到父节点的子链表中。Print_Tree
:先序遍历树的节点。SetLine_Tree
:设置父子关系。
5️⃣ 图示帮助理解
如果要可视化树的结构,TreeNode
和 ListNode
的关系如下:
TreeNode (a)
|
+--> ListNode (指向b) --> ListNode (指向c)
| |
TreeNode (b) TreeNode (c)
| |
ListNode (指向d) ListNode (指向e) --> ListNode (指向f)
|
TreeNode (d)
每个 TreeNode
的 childrenHead
指向链表的头节点,链表中的每个 ListNode
都指向一个子节点 TreeNode
。
如果有任何部分不理解的,或需要更详细的解释,请告诉我!我会为您补充细节! 😊