目录
- 一.树的概念
- 二.树的数据结构表示
一.树的概念
1.树的定义
树是n个节点的有限集合,n=0,时为空树,当n > 0时,满足如下俩个条件
(1)有且仅有一个特定的结点,称为根节点Root;
(2)当n > 1时,其余结点分为m个互不相交的有限集合,T1,T2,T3…T ( (m)),其中每一个T ( (i))又是一棵树,并且为Root的子数;
2.子树的定义
树的定义用到了递归的思想。即树的定义中,还用到了树的概念。
T (1)和T (2)就是a的子树,结点d,g,h,i组成的树又是结点
b的子树。
子树的个数没有限制,但是他们一定是互不相交的,如图所示。这样就不是树。
3.结点的定义
树的结点包含一个数据域和m个指针域,指针域用来指向它的子树。结点的分类为:根结点,叶子结点,内部结点。结点拥有子树的个数,被称为结点的度,树中各个结点度的最大值,被称为 树的度。
1)根结点
一棵树的根结点只有一个,(图中红色结点)。
2)叶子结点
度为0的结点被称为叶子结点,叶子结点不能指向任何子树(图中黄色结点)
3)内部结点
除了根结点和叶子结点以外的结点,都被称为内部结点(图中蓝色结点)
4.结点间的关系
1)孩子结点
对于某个结点,它的子树的根结点被称为该节点的孩子结点。
2)父结点
该结点被称为孩子结点的父结点
3)兄弟结点
同一父结点下的孩子结点互相称为兄弟结点。
5.树的深度
结点的层次,从根结点开始记为第1层,如果某个结点在第i层,则它的子树的根结点再第i+1层。树中结点的最大层次称为树的深度,如图所示,是一颗深度为4的树。
6.森林的定义
森林是m颗互不相交的树的集合。对于树的每一个结点而言,其子树结合就是森林,如图所示b和c俩颗子树组成的集合就是一个森林。
二.树的数据结构表示
1.结点id
为了方便树数据的读取和修改,我们一般用一个数字来表示树的结点,这个数字就是树的id,它是一个唯一的id,每个数结点的结点id不同。如图所示,每个结点都有一个id作为标识。
2.结点池
在处理数相关的问题是,结点一定是有限的,有时候也一定是确定的,比如一个问题给出的时候,给出一个n个结点的数,这个n必然是有上限的,所以我们可以事先将所有结点存储在一个顺序表,然后通过结点id的索引方式,快速获得对应结点,而这个顺序表就是结点池。
所以,根据结点id获取结点的这步操作,时间复杂度为o(1)的。
3.结点数据
数的结点数据可以是任意的,这样就可以处理任何情况下的问题,如图所示,数结点的数据的类型是字符类型(a,b,c,d,e,f,g,h,i)。
4.孩子结点列表
每个结点都要保存一个孩子结点列表(叶子结点的孩子结点列表是空的),注意这里所说的列表,不是顺序表,也不是链表,当然也不是Python中的list,而指的就是自然语义上的列表,我们可以用顺序表来实现对孩子结点的存储,也可以通过链表。
5.添加数边
如图所示,俩个绿色的箭头,分别代表的就是添加的俩条边(a->b,a->c)的过程。添加数边的过程,可以通过数的结点id先获取实际的数结点,然后将孩子结点添加到父结点的孩子结点列表来完成。
6.数的遍历
数的遍历的引入,让我们开始了解递归的概念,而数本身也是一种递归的数据结构。递归可以用来遍历这个数据结构。
三.代码
#include <stdio.h>
#include <stdlib.h>
#define eleType char
struct TreeNode;
typedef struct ListNode{
struct TreeNode* data;
struct ListNode* next;
} ListNode;
typedef struct TreeNode{
eleType data;
ListNode* childrenHead;
} TreeNode;
void AddTreeChild(TreeNode* parent, TreeNode* child) {
ListNode* childNode = (ListNode*)malloc(sizeof(ListNode));
childNode->data = child;
childNode->next = NULL;
if (parent->childrenHead == NULL) {
parent->childrenHead = childNode;
} else {
childNode->next = parent->childrenHead;
parent->childrenHead = childNode;
}
}
typedef struct Tree {
TreeNode* nodes;
TreeNode* root;
} Tree;
void TreeCreate(Tree* t, int size) {
t->nodes = (TreeNode*)malloc(sizeof(TreeNode) * size);
t->root = NULL;
for (int i = 0; i < size; i++) {
t->nodes[i].childrenHead = NULL; // 初始化子节点链表头指针
}
}
void FreeList(ListNode* head) {
ListNode* temp;
while (head != NULL) {
temp = head;
head = head->next;
free(temp);
}
}
void TreeDestroy(Tree* t) {
for (int i = 0; t->nodes[i].childrenHead != NULL; i++) {
FreeList(t->nodes[i].childrenHead);
}
free(t->nodes);
t->nodes = NULL;
t->root = NULL;
}
TreeNode* TreeGetNode(Tree* t, int id) {
return &t->nodes[id];
}
void TreeSetRoot(Tree* t, int id) {
t->root = TreeGetNode(t, id);
}
void TreeAddChild(Tree* t, int parentID, int childID) {
TreeNode* parentNode = TreeGetNode(t, parentID);
TreeNode* childNode = TreeGetNode(t, childID);
AddTreeChild(parentNode, childNode);
}
void TreeAssignData(Tree* t, int id, eleType data) {
TreeGetNode(t, id)->data = data;
}
void TreePrint(Tree* t, TreeNode* node) {
if (node == NULL) {
node = t->root;
}
printf("%c ", node->data);
ListNode* tmp = node->childrenHead;
while (tmp) {
TreePrint(t, tmp->data);
tmp = tmp->next;
}
}
int main() {
Tree T;
TreeCreate(&T, 9);
TreeSetRoot(&T, 0);
TreeAssignData(&T, 0, 'a');
TreeAssignData(&T, 1, 'b');
TreeAssignData(&T, 2, 'c');
TreeAssignData(&T, 3, 'd');
TreeAssignData(&T, 4, 'e');
TreeAssignData(&T, 5, 'f');
TreeAssignData(&T, 6, 'g');
TreeAssignData(&T, 7, 'h');
TreeAssignData(&T, 8, 'i');
TreeAddChild(&T, 0, 2);
TreeAddChild(&T, 0, 1);
TreeAddChild(&T, 1, 3);
TreeAddChild(&T, 2, 5);
TreeAddChild(&T, 2, 4);
TreeAddChild(&T, 3, 8);
TreeAddChild(&T, 3, 7);
TreeAddChild(&T, 3, 6);
TreePrint(&T, T.root);
printf("\n");
TreeDestroy(&T);
return 0;
}