树(C语言)

树的定义

树(Tree): n ( n ≥ 0 ) n(n \geq 0) n(n0)个结点构成的有限集合。当n=0时,称为空树
对于任一棵非空树 n > 0 n>0 n>0),它具有以下性质:

  • 树中有一个称为“根(Root)”的特殊结点,用r表示;

  • 其余结点可分为 m ( m > 0 ) m(m>0) m(m>0)互不相交的有限集 T 1 , T 2 , … … , T m T_1,T_2,……,T_m T1,T2,,Tm,其中每一个集合本身又是一棵树,称为原来树的“子树(SubTree)
    树与其子树

  • 子树是互不相交的;

  • 除了根节点外,每个结点有且仅有一个父节点
    不是树的几个例子:
    不是树的几个例子

  • 一棵N个节点的树有N-1条边。(除了根结点,其余节点往上都有一条边)

树的一些基本术语

  1. 结点的度(Degree):结点的子树个数(即结点下面直接连着的边的条数。)

  2. 树的度:树中所有结点中最大的度数

  3. 叶结点(Leaf)度为0的结点

  4. 父结点(Parent):有子树的的结点是其子树的根结点的父结点

  5. 子结点(Child):若A结点是B结点的父结点,则称B结点是A结点的子结点,子结点也称孩子结点。

  6. 兄弟结点(Sibling):具有同一父结点的各结点彼此是兄弟结点。

  7. 路径和路径的长度:从结点 n 1 n_1 n1 n k n_k nk的路径为一个结点序列 n 1 , n 2 , . . . , n k n_1,n_2,...,n_k n1,n2,...,nk, n i n_i ni n i + 1 n_{i+1} ni+1的父结点。路径所包含边的个数为路径的长度

  8. 祖先结点(Ancestor):沿树根到某一结点路径上的所有结点都是这个结点的祖先结点。

  9. 子孙结点(Descendant):某一结点的子树中的所有结点是这个结点的子孙。

  10. 结点的层次(Level):规定根结点在1层,其他任一结点的层数是其父结点的层数加1。

  11. 树的深度(Depth):树中所有结点中最大层次是这棵树的深度。

树的表示

树

  1. 数组实现
    很难,因为不容易判别一个结点的父结点子结点和兄弟结点等等,故一般不用。
  2. 链表实现
    1. 结点不一致
      链表实现1
      由于代码方面很难设计,所以一般不用。
    2. 结点一致
      1. 按照树的度来设计结点指针域
        若树的度为 d d d,则设计 d d d个指针域。但是有些结点不一定有 d d d个子结点,故会造成空间的浪费。
        假设有 n n n个结点,那么会被设计 n × d n \times d n×d个指针域,但是 n n n个结点只有 n − 1 n-1 n1条边,所以将会有 n × d − n + 1 n \times d-n+1 n×dn+1个指针域的空间被浪费。

      2. 儿子-兄弟表示法:每个结点设计两个指针域。第一个用来指向该结点的第一个子结点,第二个用来指向该节点的兄弟结点
        链表实现2

        针对上面的树,得到的表示图形如下:
        链表实现3

        假设有 n n n个结点,那么会被设计 2 n 2n 2n个指针域, n n n个结点有 n − 1 n-1 n1条边,所以只会有 n + 1 n+1 n+1个指针域的空间被浪费。
        将链表表示图旋转45度,将会得到一个二叉树的图。

        转变为二叉树

        由此可见,一般的树都可以用二叉树链表来实现,所以二叉树的内容在树中是很重要的,接下来着重学习二叉树。

  • 7
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目录是一种形结构,用于表示文件系统中的目录层次结构。在C语言中,可以通过结构体和指针来实现目录的表示。 首先,我们可以定义一个目录项结构体,用于表示文件或文件夹: ```c struct dir_entry { char* name; // 名称 int is_dir; // 是否是文件夹 struct dir_entry* subdirs; // 子目录 int num_subdirs; // 子目录数量 }; ``` 其中,name表示文件或文件夹的名称,is_dir表示是否是文件夹,subdirs是一个指向子目录的指针数组,num_subdirs表示子目录数量。 接下来,我们可以定义一个函数,用于创建目录项并添加到目录中: ```c void add_dir_entry(struct dir_entry* parent, struct dir_entry* child) { // 如果父目录没有子目录,先分配一个指针数组 if (parent->subdirs == NULL) { parent->subdirs = malloc(sizeof(struct dir_entry*)); parent->num_subdirs = 0; } // 扩展子目录指针数组 parent->subdirs = realloc(parent->subdirs, sizeof(struct dir_entry*) * (parent->num_subdirs + 1)); // 添加子目录 parent->subdirs[parent->num_subdirs] = child; parent->num_subdirs++; } ``` 该函数接受一个父目录和一个子目录作为参数,并将子目录添加到父目录的子目录列表中。 最后,我们可以定义一个递归函数,用于打印目录: ```c void print_dir_tree(struct dir_entry* root, int depth) { // 打印缩进 for (int i = 0; i < depth; i++) { printf(" "); } // 打印名称 printf("%s\n", root->name); // 打印子目录 for (int i = 0; i < root->num_subdirs; i++) { print_dir_tree(root->subdirs[i], depth + 1); } } ``` 该函数接受一个根目录和一个深度作为参数,并递归打印目录。 下面是一个完整的示例代码: ```c #include <stdio.h> #include <stdlib.h> struct dir_entry { char* name; int is_dir; struct dir_entry* subdirs; int num_subdirs; }; void add_dir_entry(struct dir_entry* parent, struct dir_entry* child) { if (parent->subdirs == NULL) { parent->subdirs = malloc(sizeof(struct dir_entry*)); parent->num_subdirs = 0; } parent->subdirs = realloc(parent->subdirs, sizeof(struct dir_entry*) * (parent->num_subdirs + 1)); parent->subdirs[parent->num_subdirs] = child; parent->num_subdirs++; } void print_dir_tree(struct dir_entry* root, int depth) { for (int i = 0; i < depth; i++) { printf(" "); } printf("%s\n", root->name); for (int i = 0; i < root->num_subdirs; i++) { print_dir_tree(root->subdirs[i], depth + 1); } } int main() { // 创建目录 struct dir_entry root = {"root", 1, NULL, 0}; struct dir_entry dir1 = {"dir1", 1, NULL, 0}; struct dir_entry dir2 = {"dir2", 1, NULL, 0}; struct dir_entry file1 = {"file1", 0, NULL, 0}; struct dir_entry file2 = {"file2", 0, NULL, 0}; add_dir_entry(&root, &dir1); add_dir_entry(&root, &dir2); add_dir_entry(&dir1, &file1); add_dir_entry(&dir2, &file2); // 打印目录 print_dir_tree(&root, 0); return 0; } ``` 输出结果为: ``` root dir1 file1 dir2 file2 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值