数据结构——树

Posted cuizhu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构——树相关的知识,希望对你有一定的参考价值。

参考:Mark Allen Weiss 著《数据结构与算法分析——C语言描述》(第二版)

1 树的定义

  一棵树是一些节点的集合。这个集合可以是空集;若非空,则是一棵树由称作根的节点r以及0个或者多个非空的子树组成。这些子树中每一棵的根都被来自根 r的一条有向的边所连接。图1 就是一棵具体的树,

    技术分享图片

                图1 一棵具体的树

 

2 树中的基本术语

  2.1 叶子节点
    没有儿子的节点


  2.2 兄弟节点
    具有相同父亲的节点


  2.3 深度(deep)
    对任意节点n的深度 = 根到节点n的唯一路径的长
    补充:根的深度为0


  2.4 高度
    节点X处的高度是从X到一片树叶的最长路径的长
    补充:一棵树的高度 = 它的根的高

  2.5 举例说明

  在图1中,根节点为A,A的儿子节点为B、C、D、E、F、G。叶子节点有B、C、H、I、P、Q、K、L、M、N。互为兄弟节点的有K、L和M。K、L和M的父节点为F。图1 是一棵高度为3的树。图中还有其它关系,这里就不一一举例了,有兴趣的话,可以自己找一找。

 

3 树的实现

  3.1 实现思路

    每一个节点除数据外还要一些指针,使得该节点的每一个儿子都有一个指针指向它。由于每个节点的儿子数可以变化很大且事先不知道。

  因此,在数据结构中建立到各儿子节点直接的链接是不可行的,此方法会浪费太多的空间。可以将每个节点的所有儿子都放在树节点的链表中。

 

  3.2 实现的伪代码

typedef struct TreeNode *PtrToNode;

struct TreeNode
{
    ElementType Element;        //存储数据
    PtrToNode FirstChild;          //指向第一个儿子节点的指针
    PtrToNode NextChild;          //指向下一兄弟节点的指针
};

 

4 树的基本操作

  如果一个树只能存储而无法读取,也就无法使用。那么我们还设计这种结构还有什么意义!遍历又分为先序遍历(preorder traversal)和后序遍历(postorder traversal)两种。

  (1)先序遍历(preorder traversal)思想:对节点的处理工作是在它的诸儿子节点被处理之前进行的。

  (2)后序遍历(postorder traversal)思想:在一个节点处的工作是在它的诸儿子节点被计算后进行的

 

5 树的应用

  包括UNIX、VAX/VMS 和DOS在内的许多常用操作系统中的目录结构。下面列举两个案例来说明:“列出分级文件系统中目录的例程”和“计算一个目录大小的例程”。

  5.1 伪代码

    (1)“列出分级文件系统中目录的例程”的伪代码如下, 

static void ListDir(DirectoryOrFile D, int Depth)
{
    if(D is a legitimate entry)
    {
        PrintName(D, Depth);
        if(D is a directory)
            for each child, C of D:
                ListDir(C, Depth + 1);
    }
}

void ListDirectory(DirectoryOrFile D)
{
    ListDir(D, 0);
}

 

    (2)“计算一个目录大小的例程”的伪代码如下,

static int SizeListDirectory(DirectoryOrFile D)
{
    int TotalSize;
    
    TotalSize = 0;
    if(D is a directiry)
        for each child, C of D:
            TotalSize += SizeListDirectory(C);
    return TotalSize;
}

 

  5.2 python语言实现

    (1)“列出分级文件系统中目录的例程”的python语言实现代码如下, 

 1 import os
 2 
 3 
 4 # 按照一定格式打印文件或目录名称
 5 def listDir(dir, num):
 6     """
 7     :param dir: absolute path
 8     :param num: the number of "/" before your firstinput path
 9     :return: None
10     """
11     # 1.列举出,当前给定的文件夹下的所有文件,以及子文件
12     file_list = os.listdir(dir)
13 
14     # 2. 获取当前路径下文件夹名称并打印
15     # 2.1 获取当前文件夹名称
16     index = dir.rfind("/")
17     if index == -1:
18         file = dir
19     file = dir[index + 1:]
20     # 2.2 打印文件名及
21     print(file)
22 
23     # 3.针对于列举出的列表,进行遍历
24     for fname in file_list:
25         # 3.1 拼接当前文件的路径
26         new_fname = dir + "/" + fname
27         # 3.2 控制每行打印时的段前空格数
28         sizedepth = new_fname.count("/") - num
29 
30         # 3.3 判断是否为目录
31         if os.path.isdir(new_fname):
32             print(end="	" * sizedepth)
33             listDir(new_fname, num)
34         else:
35             # 打印文件名称
36             print("	" * (sizedepth) + fname)
37 
38 # 测试代码
39 
40 
41 if __name__ == __main__:
42     path = "F:/python_AI/python基础/python文件操作/files1"
43     num = path.count("/")
44     print("num = ", num)
45     listDir(path, num)

 

  执行后结果如图2:

        技术分享图片

             图2 打印目录结果

       

    (2)“计算一个目录大小的例程”的python语言实现代码如下,

         (i)文件大小计算模块sizedir.py文件,文件内容如下:     

 1 import os
 2 
 3 totalSize = 0
 4 # 计算文件或目录下的所占大小
 5 def cal_size(path):
 6     """
 7     :param path: an absolute path
 8     :return: the size of the input path(file or directory)
 9     """
10     if not os.path.isdir(path):
11         print(Error:", path, " is not a directory or does not exist.)
12         return
13     global totalSize
14     for lists in os.listdir(path):
15         sub_path = os.path.join(path, lists)
16         # print(sub_path)
17         if os.path.isfile(sub_path):
18             totalSize = totalSize+os.path.getsize(sub_path)  # 文件总大小
19         elif os.path.isdir(sub_path):
20             cal_size(sub_path)                           # 递归遍历子文件夹
21     return totalSize
22 
23 # KBG单位的转换
24 def sizeConvert(size):                                   # 单位换算
25     """
26     :param size: a number,int
27     :return: a number,float,Keep three decimal places after the decimal point.
28     """
29     K, M, G = 1024, 1024**2, 1024**3
30     if size >= G:
31         return str(round(size/G, 3))+ G Bytes
32     elif size >= M:
33         return str(round(size/M, 3))+ M Bytes
34     elif size >= K:
35         return str(round(size/K, 3))+ K Bytes
36     else:
37         return str(size)+ Bytes

    (ii)打印文件名称及大小模块my_size_dir.py,文件内容如下,

 1 import os
 2 import sizedir
 3 
 4 # 打印文件或目录名称及所占内存大小
 5 def listDir(dir, num):
 6 
 7     # 1.列举出,当前给定的文件夹下的所有文件,以及子文件
 8     file_list = os.listdir(dir)
 9 
10     # 2. 获取当前路径下文件夹名称并打印
11     # 2.1 获取当前文件夹名称
12     index = dir.rfind("/")
13     if index == -1:
14         file = dir
15     file = dir[index + 1:]
16     # 2.2 计算当前目录下的文件总大小
17     dir_size = sizedir.cal_size(dir)
18     # 2.3 打印文件名及文件大小
19     print(file, "	(%s)" % sizedir.sizeConvert(dir_size))
20 
21     # 3.针对于列举出的列表,进行遍历
22     for fname in file_list:
23         # 3.1 拼接当前文件的路径
24         new_fname = dir + "/" + fname
25         # 3.2 控制每行打印时的段前空格数
26         sizedepth = new_fname.count("/") - num
27 
28         # 3.3 判断是否为目录
29         if os.path.isdir(new_fname):
30             print(end="	" * sizedepth)
31             listDir(new_fname, num)
32         else:
33             # 打印文件名称及大小
34             fsize = sizedir.sizeConvert(os.path.getsize(new_fname))
35             print("	" * (sizedepth) + fname, "	(%s)" % fsize)
36 
37 # 测试代码
38 if __name__ == __main__:
39     path = "F:/python_AI/python基础/python文件操作/files1"
40     num = path.count("/")
41     # print(num)
42     listDir(path, num)

  上面代码执行结果如图3:

    技术分享图片

             图3 打印目录及大小结果

6 树的分类

  树的分类如图4。

    技术分享图片

                                   图4 树的分类











以上是关于数据结构——树的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中绘制回归树

LeetCode810. 黑板异或游戏/455. 分发饼干/剑指Offer 53 - I. 在排序数组中查找数字 I/53 - II. 0~n-1中缺失的数字/54. 二叉搜索树的第k大节点(代码片段

使用 Apollo 客户端的片段组合:约定和样板

Discord.py 如何制作干净的对话树?

VSCode自定义代码片段5——HTML元素结构

VSCode自定义代码片段5——HTML元素结构