varch/doc/tree.md

406 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## 介绍
### 一、基本概念
1. **定义**树是一种非线性的数据结构由nn≥0个节点组成每个节点可以有零个或多个子节点但只有一个父节点根节点除外。这种结构类似于自然界中的树具有明显的层次性[^1^][^2^][^3^]。
2. **特点**
- 树具有根节点和叶节点。根节点是没有父节点的节点,而叶节点是没有子节点的节点[^1^][^2^][^3^]。
- 除了根节点外,每个节点有且仅有一个父节点[^1^][^2^][^3^]。
- 树的度是指树中所有节点的最大度数,即节点拥有的最大子节点数[^2^][^3^]。
- 树的高度或深度是指从根节点到最远叶节点的最长路径上的边数[^2^][^3^]。
### 二、基本术语
1. **节点的度**:节点拥有的子节点数称为节点的度[^2^][^3^]。
2. **叶节点**度为0的节点称为叶节点[^2^][^3^]。
3. **非终端节点或分支节点**度不为0的节点称为分支节点[^2^][^3^]。
4. **双亲节点或父节点**:若一个节点含有子节点,则这个节点称为其子节点的父节点[^2^][^3^]。
5. **孩子节点或子节点**:一个节点含有的子树的根节点称为该节点的子节点[^2^][^3^]。
6. **兄弟节点**:具有相同父节点的节点互称为兄弟节点[^2^][^3^]。
7. **祖先节点**:从根到该节点所经分支上的所有节点都是这个节点的祖先节点[^2^][^3^]。
8. **子孙**:以某节点为根的子树中任一节点都称为该节点的子孙[^2^][^3^]。
9. **森林**由mm>0棵互不相交的树的集合称为森林[^2^][^3^]。
### 三、表示方法
树的表示方法有多种,包括双亲表示法、孩子表示法、孩子兄弟表示法等。其中,孩子兄弟表示法是最常用的一种表示方法,它将整棵树用二叉链表存储起来,左指针指向自己的第一个孩子,右指针指向与自己相邻的兄弟[^1^][^2^][^3^]。
### 四、特殊类型
1. **二叉树**:二叉树是一种特殊的树,它的每个节点最多有两个子节点,通常称为左子节点和右子节点[^5^]。
2. **平衡树**平衡树是一种特殊的二叉树它的左右子树的高度差不超过1。常见的平衡树包括AVL树、红黑树等[^5^]。
### 五、遍历方法
树的遍历方法主要有前序遍历、中序遍历、后序遍历和层序遍历。这些遍历方法可以帮助我们访问树中的每一个节点,并根据需要进行处理[^5^]。
### 六、实际应用
树在实际中有着广泛的应用,如文件系统、数据库索引、编译器设计等[^5^]。在文件系统中,树形结构用于组织和管理文件;在数据库中,树形结构用于构建高效的索引;在编译器设计中,树形结构用于表示程序的语法结构等。
## 接口
### 创建和删除tree对象
```c
tree_t tree_create(void);
void tree_delete(tree_t tree, void (*func)(tree_t tree));
```
创建方法:
用于创建一个空树,即初始化一个树结构的实例,为后续向树中添加节点、设置数据等操作做准备。若创建成功,会返回该树的句柄(`tree_t`类型),后续对这棵树的其他操作都可以通过该句柄来进行;若创建过程中出现内存分配失败等问题导致创建失败,则返回`NULL`。该函数无输入参数,直接调用即可尝试创建一个新的空树结构。
删除方法:
负责删除指定的树结构,释放树及其所有节点占用的内存资源,同时还可以传入一个额外的执行函数,在删除树的过程中针对每个节点执行一些自定义的清理操作(比如释放节点中特定资源等),以确保树相关的所有资源都能被妥善处理,避免出现内存泄漏等问题。此函数无返回值,执行完相应的删除和资源释放操作后即完成任务。
- `tree`:要删除的树的句柄,通过之前`tree_create`函数创建或者其他途径获取的树句柄来指定具体要删除的是哪棵树,确保准确地对相应树及其关联的所有资源进行清理操作。
- `func`:指向一个函数的指针,该函数接受树的句柄作为参数,用于在删除树的每个节点时执行额外的自定义操作,若不需要执行额外操作,可以传入`NULL`作为该参数。
```c
static void test_create(void)
{
tree_t tree = tree_create();
if (tree)
{
printf("tree create success!!!\r\n");
}
else
{
printf("[ERROR] tree create fail!!!\r\n");
}
tree_delete(tree, NULL);
}
```
### tree的设置和获取数据
```c
int tree_set_data(tree_t tree, void* data, int size);
int tree_get_data(tree_t tree, void* data, int size);
const void* tree_data(tree_t tree);
```
设置:
用于将给定的数据设置到指定树节点中会覆盖原有的数据内容如果传入的数据大小为0则会删除原有的数据。若设置数据操作成功函数返回1若树节点不存在、传入的参数不合法等情况导致设置失败则返回0。
- `tree`:树的句柄,通过该句柄定位到要设置数据的目标树节点,确保数据能准确设置到对应的节点中。
- `data`:指向要设置的数据的指针,该指针指向的数据内容会被复制到树节点中,替换原有的数据,其数据类型和大小应符合树节点对数据的要求。
- `size`要设置的数据的大小以字节为单位若该值为0则执行删除原数据的操作否则按照该大小将新数据复制到树节点中。
获取:
从指定树节点中获取数据并将获取到的数据复制到传入的指针所指向的内存空间中若获取操作成功函数返回1若树节点不存在数据、传入的参数不合法如传入的内存空间大小不足以存储数据等导致获取失败则返回0。
- `tree`:树的句柄,依据该句柄找到对应的树节点,从该节点中获取数据内容,确保准确获取目标节点的数据。
- `data`:指向用于存储获取的数据的内存空间的指针,该指针指向的内存空间需要有足够的容量来存放从树节点获取的数据,其数据类型也应与树节点的数据类型匹配。
- `size`:期望获取的数据大小,以字节为单位,需要与树节点实际存储的数据大小相匹配,否则可能导致获取失败。
```c
static void test_set(void)
{
tree_t tree = tree_create();
int set = 100;
int get = 0;
tree_set_data(tree, &set, sizeof(set));
tree_get_data(tree, &get, sizeof(get));
printf("get %d\r\n", get);
printf("data %d\r\n", *(int *)tree_data(tree));
tree_delete(tree, NULL);
}
```
### tree插入和擦除子节点
```c
int tree_insert(tree_t tree, int index);
int tree_erase(tree_t tree, int index);
```
tree_insert
在指定树的指定索引位置插入一个空的子空间用于后续向该位置添加子树等操作。若插入操作成功函数返回1若索引超出范围、树不存在或者出现其他导致插入失败的情况则返回0。
- `tree`:树的句柄,用于定位要进行插入操作的目标树,只有对应正确的树才能准确找到插入的位置并执行插入操作。
- `index`要插入空孩子空间的索引位置该索引表示树中节点的位置序号从0开始计数通过该索引确定在树的哪个位置插入新的子空间不过要确保传入的索引值在树的有效范围内小于当前节点数等合理范围
tree_erase
用于擦除指定树中指定索引位置的空孩子空间。若擦除操作成功函数返回1若索引不合法、对应位置不存在空孩子空间或者树本身存在问题等导致擦除失败则返回0。
- `tree`:树的句柄,通过该句柄确定要进行擦除操作的目标树,依据树的结构找到对应的索引位置来执行擦除空孩子空间的操作。
- `index`:要擦除的空孩子空间所在的索引位置,与`tree_insert`中的索引类似,需在树的有效索引范围内,以此准确定位到要处理的目标位置。
```c
static void test_insert(void)
{
tree_t tree = tree_create();
tree_insert(tree, 0);
tree_insert(tree, 1);
tree_insert(tree, 2);
tree_insert(tree, 3);
printf("tree child size %d\r\n", tree_csize(tree));
tree_erase(tree, 2);
printf("tree child size %d\r\n", tree_csize(tree));
tree_delete(tree, NULL);
}
```
### tree附着和分离子节点
```c
int tree_attach(tree_t tree, int index, tree_t attach);
tree_t tree_detach(tree_t tree, int index);
```
tree_attach
将一个独立的树附着到另一个树的指定索引位置上实现树结构的组合与扩展构建更复杂的树状层次结构。若附着操作成功函数返回1若索引不合法、目标树或要附着的树存在问题如句柄无效等导致附着失败则返回0。
- `tree`:目标树的句柄,即要将其他树附着到的那个树的句柄,通过它确定附着的目标位置所在的树结构主体。
- `index`:在目标树中的索引位置,用于指定将独立树附着到目标树的哪个位置节点下,同样要保证索引值在目标树的有效范围内。
- `attach`:要附着的独立树的句柄,指向另一个已经创建好的树结构,该树会被添加到目标树的指定位置,成为目标树的一部分。
tree_detach
从指定树的指定索引位置分离出子树,返回分离出来的子树句柄,若分离操作成功,可通过返回的句柄对分离出的子树进行后续操作;若索引不合法、对应位置没有子树或者树本身存在问题等导致分离失败,则返回`NULL`。
- `tree`:树的句柄,用于定位要进行分离操作的原始树,从该树的指定索引位置尝试分离出子树。
- `index`:要分离子树所在的索引位置,通过该索引准确找到目标子树在原始树中的位置,进行分离操作,需确保索引是有效的。
```c
static void test_attach(void)
{
tree_t tree = tree_create();
tree_t node = NULL;
int data = 0;
tree_insert(tree, 0);
tree_insert(tree, 1);
tree_insert(tree, 2);
tree_insert(tree, 3);
for (int i = 0; i < tree_csize(tree); i++)
{
node = tree_create();
data = 1000 + i;
tree_attach(tree, i, node);
tree_set_data(node, &data, sizeof(data));
}
tree_print(tree, 0, print_int);
node = tree_detach(tree, 1);
tree_delete(node, NULL);
tree_print(tree, 0, print_int);
tree_delete(tree, NULL);
}
```
### tree获取相关节点
```c
tree_t tree_parent(tree_t tree);
tree_t tree_child(tree_t tree, int index);
```
tree_parent
用于获取指定树的父树句柄,若该树存在父树,则返回父树的句柄,通过该句柄可以进一步操作父树;若该树没有父树(比如是根节点所在的树或者本身就是独立的树等情况),则返回`NULL`。
- `tree`:树的句柄,通过它来定位要查找父树的目标树,依据树的层次结构去寻找对应的父树并返回其句柄。
tree_child
获取指定树中指定索引位置的子树句柄,若索引位置存在子树,则返回对应的子树句柄,方便后续对子树进行相关操作;若索引不合法或者对应位置没有子树,则返回`NULL`。
- `tree`:树的句柄,用于确定要查找子树的目标树,在该树的结构内依据索引去定位和获取相应的子树句柄。
- `index`子树在目标树中的索引位置从0开始计数通过该索引在目标树中查找对应的子树需保证索引在有效范围内。
```c
static void test_parent(void)
{
tree_t tree = tree_create();
tree_t node = NULL;
tree_t parent, child;
tree_insert(tree, 0);
tree_insert(tree, 1);
tree_insert(tree, 2);
tree_insert(tree, 3);
tree_set_data(tree, "Parent", 7);
node = tree_create(); tree_set_data(node, "Child0", 7); tree_attach(tree, 0, node);
node = tree_create(); tree_set_data(node, "Child1", 7); tree_attach(tree, 1, node);
node = tree_create(); tree_set_data(node, "Child2", 7); tree_attach(tree, 2, node);
node = tree_create(); tree_set_data(node, "Child3", 7); tree_attach(tree, 3, node);
tree_print(tree, 0, print_string);
parent = tree_parent(node); print_string(parent); printf("\r\n");
child = tree_child(tree, 0); print_string(child); printf("\r\n");
child = tree_child(tree, 1); print_string(child); printf("\r\n");
child = tree_child(tree, 2); print_string(child); printf("\r\n");
child = tree_child(tree, 3); print_string(child); printf("\r\n");
tree_delete(tree, NULL);
}
```
### tree设置和获取属性
```c
int tree_set_attribute(tree_t tree, void* attribute, int size);
int tree_get_attribute(tree_t tree, void* attribute, int size);
const void* tree_attribute(tree_t tree);
```
tree_set_attribute
将给定的属性数据设置到指定树节点中会覆盖原有的属性内容若传入的属性数据大小为0则会删除原有的属性数据。若设置属性操作成功函数返回1若树节点不存在、参数不合法等情况导致设置失败则返回0。
- `tree`:树的句柄,通过该句柄定位到要设置属性的目标树节点,保证属性能准确设置到对应的节点中。
- `attribute`:指向要设置的属性数据的指针,该指针指向的数据内容会被复制到树节点中,替换原有的属性数据,其数据类型和大小应符合树节点对属性的要求。
- `size`要设置的属性数据的大小以字节为单位若该值为0则执行删除原属性数据的操作否则按照该大小将新属性数据复制到树节点中。
tree_get_attribute
从指定树节点中获取属性数据并将获取到的属性数据复制到传入的指针所指向的内存空间中若获取操作成功函数返回1若树节点不存在属性数据、传入的参数不合法如传入的内存空间大小不足以存储属性数据等导致获取失败则返回0。
**参数介绍**
- `tree`:树的句柄,根据该句柄找到对应的树节点,从该节点中获取属性数据内容,确保准确获取目标节点的属性数据。
- `attribute`:指向用于存储获取的属性数据的内存空间的指针,该指针指向的内存空间需要有足够的容量来存放从树节点获取的属性数据,其数据类型也应与树节点的属性数据类型匹配。
- `size`:期望获取的属性数据大小,以字节为单位,需要与树节点实际存储的属性数据大小相匹配,否则可能导致获取失败。
tree_attribute
获取指定树节点的属性信息的地址,若节点存在有效的属性数据,则返回其地址指针,方便后续对属性数据进行访问和操作;若节点没有属性或者不存在等情况,则返回`NULL`。
- `tree`:树的句柄,利用该句柄定位到具体的树节点,从而获取其关联的属性信息地址,不同树节点的属性情况可能不同,通过句柄能准确获取对应节点的属性数据地址。
```c
static void test_attr(void)
{
tree_t tree = tree_create();
char attr[20];
tree_set_attribute(tree, "This attribute!!!", 18);
tree_get_attribute(tree, attr, sizeof(attr));
printf("attr %s\r\n", attr);
tree_delete(tree, NULL);
}
```
### tree的深度
```c
int tree_depth(tree_t tree);
```
获取指定树的深度,深度计算包含树本身所在的层级,返回的整数值表示从根节点到最远叶子节点的层级数,可用于了解树的层次结构复杂程度等情况。
- `tree`:树的句柄,通过该句柄定位到对应的树结构,进而计算并返回其深度信息,不同树的深度因结构不同而有差异,通过句柄能准确获取对应树的深度情况。
```c
static void test_depth(void)
{
tree_t root = tree_create();
tree_t node, temp;
tree_insert(root, 0);
tree_insert(root, 1);
tree_insert(root, 2);
tree_attach(root, 0, tree_create());
tree_attach(root, 1, tree_create());
node = tree_child(root, 0);
tree_insert(node, 0);
tree_insert(node, 1);
node = tree_child(root, 1);
tree_insert(node, 0);
tree_insert(node, 1);
tree_attach(node, 0, tree_create());
tree_attach(node, 1, tree_create());
tree_print(root, 0, NULL);
printf("depth %d\r\n", tree_depth(root));
tree_delete(root, NULL);
}
```
### tree连续获取子节点
```c
tree_t tree_to_valist(tree_t tree, int index,...);
#define tree_to(tree, i, ...)
```
通过连续的索引值来获取树中的子树,从指定树开始,按照传入的索引顺序依次查找子树,直到遇到负数索引停止查找,若能成功找到对应的子树,则返回最终的子树句柄;若在查找过程中出现索引不合法、对应位置无子树等情况导致无法找到目标子树,则返回`NULL`。`tree_to`为更简单的宏定义版本,默认在参数末尾添加了负数。
- `tree`:树的句柄,用于确定起始查找的树结构,后续的索引查找都是基于这个树开始进行的,是整个查找操作的基础起点。
- `index`:第一个索引值,从该索引位置开始在树中查找子树,后续还可以传入更多的索引值(通过可变参数实现),不过要保证每个索引都是合法有效的,且在对应的树结构范围内。
- `...`:可变参数部分,表示后续的其他索引值,这些索引值是连续使用的,用于在找到的子树中继续查找下一级子树,直到遇到负数索引停止查找过程。
```c
static void test_to(void)
{
tree_t root = tree_create();
tree_t node, temp;
tree_insert(root, 0);
tree_insert(root, 1);
tree_insert(root, 2);
tree_attach(root, 0, tree_create());
tree_attach(root, 1, tree_create());
node = tree_child(root, 0);
tree_insert(node, 0);
tree_insert(node, 1);
node = tree_child(root, 1);
tree_insert(node, 0);
tree_insert(node, 1);
tree_attach(node, 0, tree_create());
tree_attach(node, 1, tree_create());
temp = tree_to(root, 1, 1);
tree_set_data(temp, "To function!!!", 15);
tree_print(root, 0, print_string);
tree_delete(root, NULL);
}
```
### tree大小
```c
int tree_csize(tree_t tree);
int tree_dsize(tree_t tree);
int tree_asize(tree_t tree);
int tree_size(tree_t tree);
```
tree_csize
用于获取指定树的子树数量,也就是该树拥有的孩子节点个数,返回的整数值可以帮助开发者了解树的分支情况,例如判断树的某个节点是否有子节点等。
tree_dsize
获取指定树节点存储的数据大小,返回的整数值表示对应节点数据所占用的字节数,有助于了解数据的规模以及进行一些与数据大小相关的操作判断等。
tree_asize
获取指定树节点的属性数据大小,返回的整数值表示对应节点属性信息所占用的字节数,可用于了解属性数据的规模以及相关操作时的判断依据等。
tree_size
获取指定树的总节点数量,返回的整数值代表整棵树包含的所有节点个数,能直观体现树的规模大小,方便开发者在进行一些遍历、统计等操作时参考树的整体规模情况。
### tree打印
```c
void tree_print(tree_t tree, int depth, void (*print)(tree_t tree));
```
用于按照指定的深度对树结构进行扩展打印展示,通过传入的打印函数(由开发者自定义具体的打印逻辑)来输出树的相关信息,比如节点数据、层次关系等内容,若`depth`参数为0则会打印整棵树的全部信息若传入具体的深度值则按照该深度进行相应层级的打印。此函数无返回值主要用于可视化展示树的结构情况。
- `tree`:树的句柄,通过该句柄确定要进行打印展示的目标树结构,不同的树有各自独立的结构和数据,通过句柄能准确区分并打印对应的树信息。
- `depth`要打印的深度以层级数表示取值为0表示打印整棵树的所有层级信息若传入大于0的整数则按照该层级数从根节点开始向下打印相应层数的树结构信息。
- `print`:指向一个自定义打印函数的指针,该函数接受树的句柄作为参数,开发者需要在该函数内部实现具体的打印逻辑,比如按照何种格式输出节点数据、如何体现层次关系等,以此来实现个性化的树结构打印展示效果。