【数据结构与算法分析】0基础带你学数据结构与算法分析06--树(TREE)

news2024/11/18 4:50:04

目录

前言

树的属性 

树的实现

树的遍历与应用

深度有限遍历 (DFS)

广度优先遍历 (BFS) 


Not all roots are buried down in the ground, some are at the top of a tree.

— Jinvirle

前言

Tree 是一些结点的集合,这个集合可以是空集;若不是空集,则 Tree 是由称为 的结点 r 以及零或多个非空的子树 T1,T2,⋯ ,​ 组成,这些子树的根都与 r 有一条有向边 (edge) 连接。这些子树的根被称为根 r 的孩子 (child),而 r 是这些 child 的父亲 (parent)。 

树的属性 

根据给出的树的递归定义,可以发现一个树是由 N 个 node 和 N−1 条 edge 的集合。而除 root 外的所有 node 都有一个由其 parent 指向它的 edge。在树中有一些特殊的属性是需要注意的,这里先给出相关概念与示例,如果不是很理解,可以通过结合示例来理解这些概念。

结点的度 (degree)

        一个节点含有的子树的个数称为该节点的度

树的度 (degree of tree)

        一棵树中最大的 node degree 称为树的度

叶结点 (leaf)

        或称终端结点,如果结点满足 degree=0 则该结点为叶结点

分支结点 (branch node)

        或称内部结点 (internal node)、非终端结点,度不为 0 的结点

层次 (level)

        从 root 开始,root 所在的层为第 1 层,root 的 child 为第二层,以此类推

关系

        树就像一本族谱,从 root 开始结点直接有一定的亲缘关系

  • 兄弟 (sibling): 具有相同父节点的节点互为兄弟节点
  • 叔父 (uncle): 父结点的兄弟结点为该结点的叔父结点
  • 堂兄弟: 父结点在同一层的结点互为堂兄弟

 

路径 (path)

结点 n1,n2,⋯ ,nk​ 的一个序列,使得对于 1≤i<k 满足 ni 是 ni+1 的 parent,则这个序列被称为从 n1​ 到结点 nk​ 的 path。其路径长度 (length) 为路径上的 edge 的数量,即 k−1 。特别地,每个结点到自己的 path lenth 为 0

深度 (depth)

对于结点 ni​ ,从 root 到 ni​ 的唯一路径的长度 (Depthroot=0)

高度 (height)

对于结点 ni​ ,从 ni 到 leaf 的最长路径长度 (Heightleaf=0)

树的高度

或称树的深度,其总是等于根的高度,或最深的结点的深度,可以认为一棵空树的高度为 −1

祖先 (ancestor)

对于结点 ni 与 nj 存在一条 ni​ 到 nj​ 的路径,那么称 ni​ 是 nj​ 的祖先 (ancestor),而 nj​ 是 ni 的 后裔 (descendant)

距离 (distance)

对于结点 ni​ 与 nj ,从最近的公共祖先结点 nk​ 分别到它们的路径长度之和被称为距离 (distance)。特别地,如果 ni=nk​ ,则 ni 与 nj 的距离为 ni​ 到 nj 的路径的长度

 注:

 严蔚敏老师的数据结构中,或者往常的实现中,根的高度为 1,而叶的深度也为 1,树的高度一般指其最大的层次,因此认为空树的高度为 0。

 树的实现

实现树的一种方法是在每一个结点上,除数据外还需要一些链域来指向该结点的每个子结点,然而由于每个结点的子结点数量是不确定的,我们不能直接建立到各个子结点的直接链接。如果申请一定大小的空间以存放子结点,则可能会造成空间的浪费,或不足。因此我们链表的形式存储子结点,而父结点中只存储第一个子结点的指针,如果该链域为空则意味着该结点是叶结点 (degree=0。每个结点中存在一个指向其下一个兄弟的指针,为遍历父结点的所有孩子提供了方法,当该结点 next_sibling=nullptr 时意味着这是父结点的最后一个子结点。 

struct TreeBaseNode {
  TreeBaseNode* first_child;
  TreeBaseNode* next_sibling;
};
template <class Element>
struct TreeNode {
  Element data;
};

如果我们用这个结构实现上述图示的树,可以画一下其表示。

 可以发现,除非该结点是 leaf,否则我们很难判断该结点的 degree。且在计算深度与距离时,要十分小心在兄弟间步进,因为兄弟间步进并不会增加其与 parent 的距离。

树的遍历与应用

观察你系统中的文件系统,回到文件系统的顶层 / (root),并浏览一些目录你会发现, 整个目录结构与 tree 是类似的,我们也常常将其称为目录树。

 这颗目录树稍微有些复杂了,不过问题不大。一般文件系统中采用路径名来访问一个文件,而我们可以像遍历树一样遍历这个文件系统,将每个文件打印出来,并按照层级来缩进文件名称。

深度有限遍历 (DFS)

给出一个代码实现:

void filesystem::list_all(file& f, int depth = 0) const {
  print_name(f, depth);  // 打印文件的名称
  if (is_directory(f)) {
    for (file p : get_file_list(f)) { // 遍历目录中的每个文件
      list_all(p, depth + 1);
    }
  }
}

最终的输出结果可能是:

/
 |--- mnt/
 |--- home/
       |--- GinShio/
 |--- usr
       |--- LICENSE
       |--- lib/
             |--- libQt5Core.so
             |--- X11/
                   |--- display-manager
                   |--- etc/
                   |--- displaymanagers/
                         |--- console
                         |--- lightdm
                         |--- sddm
                         |--- xdm
             |--- libstdc++.so.6
             |--- mozilla/
                   |--- kmozillahelper
       |--- bin/
             |--- latexmk
             |--- pdftk
             |--- zsh
.....
.....

在遍历中,每访问一个结点时,对结点的处理工作总是比其子结点的处理先进行,这种先处理根再处理子结点的策略被称为 前序遍历 (preorder traversal)。而另一种常用的遍历方法是 后序遍历 (postorder traversal),即在结点的所有子结点处理完成后再对其进行处理。无论这两种遍历的哪一个,在遍历这个树时总是可以在 O(N) 的时间复杂度里完成。对于目录的 postorder traversal 留给读者思考并实现。

现在考虑这两种算法有什么共通的特点。有没有发现它们都是在一棵子树上处理完所有结点之后再转移到另一棵子树上,这种一直向着 child 递归,直到全部递归结束时再向 sibling 递归的算法,就被称之为 深度优先搜索 (Depth-first Search, DFS)。由于 DFS 使用递归算法,因此 DFS 总能被改写为 loop,非 tail recursion 的递归有可能需要 stack 的帮助才能改为 loop。

 

广度优先遍历 (BFS) 

请回看 树的实现 一节的图,图中的树如果以一层一层遍历,当一层的所有结点都被遍历完时,再进入更深一层,从这层的第一个结点开始处理。这种遍历方式被称为 广度优先遍历 (Breadth-first Search, BFS) 或者是层序遍历。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【模型训练】YOLOv7行人检测

YOLOv7行人检测 1、YOLOv7算法行人检测模型训练2、YOLOv7模型模型评估3、模型和数据集下载1、本项目采用YOLOv7算法实现对行人的检测和识别,在一万多张行人检测数据集中训练得到,我们训练了YOLOv7模型,经评估我们得出了各个模型的评价指标; 2、目标类别数:1 ;类别名:pers…

笔试强训第15天(手套+ 查找输入整数二进制中1的个数)

选择 C barfoob_bar new B 会先创建一个B类对象&#xff0c;B类对象的构造需要调用B的构造函数&#xff0c;从而调用A的构造函数。A的构造函数中调用了 bar()函数&#xff0c;该函数虽然重写了&#xff0c;但这里不构成多态调用。因为虚表中的函数指针是在构造函数的初始化列表…

爱上源码,重学Spring IoC深入

回答&#xff1a; 我们为什么要学习源码&#xff1f; 1、知其然知其所以然 2、站在巨人的肩膀上&#xff0c;提高自己的编码水平 3、应付面试1.1 Spring源码阅读小技巧 1、类层次藏得太深&#xff0c;不要一个类一个类的去看&#xff0c;遇到方法该进就大胆的进 2、更不要一行…

【3D目标检测】SECOND: Sparsely Embedded Convolutional Detection

目录概述细节网络结构稀疏卷积方向分类损失函数概述 首先&#xff0c;本文是基于点云&#xff0c;并且将点云处理成体素的3D目标检测网络&#xff0c;提出的SECOND可以看做是VoxelNet的升级版。 提出动机与贡献 VoxelNet计算量比较大&#xff0c;速度比较慢&#xff08;训练和…

第二节:数据类型与变量【java】

目录 &#x1f4c3;前言 &#x1f4d7;1.数据类型 &#x1f4d5;2. 变量 2.1 变量概念 2.2 语法格式 &#x1f4d9;3.整型变量 3.1 整型变量 3.2 长整型变量 3.3 短整型变量 3.4 字节型变量 &#x1f4d8;4.浮点型变量 4.1 双精度浮点型 4.2 单精度浮点型 &#…

[SpringBoot] AOP-AspectJ 切面技术

✨✨个人主页:沫洺的主页 &#x1f4da;&#x1f4da;系列专栏: &#x1f4d6; JavaWeb专栏&#x1f4d6; JavaSE专栏 &#x1f4d6; Java基础专栏&#x1f4d6;vue3专栏 &#x1f4d6;MyBatis专栏&#x1f4d6;Spring专栏&#x1f4d6;SpringMVC专栏&#x1f4d6;SpringBoot专…

python的编译器与解释器

作者介绍&#xff1a; &#x1f425;作者&#xff1a;小刘在C站 &#x1f446;每天分享课堂笔记&#xff0c;一起努力&#xff0c;共赴美好人生 &#x1f341;夕阳下&#xff0c;是最美的绽放 目录 一.为什么会有编译器和解释器 二.编译器和解释器的区别 三.python解释器种类…

RK3399应用开发 | 移植libdrm到rk3399开发板(2.4.113)

一、下载源码 下载地址:https://dri.freedesktop.org/libdrm/。 这里我下载最新的2.4.113版本: wget https://dri.freedesktop.org/libdrm/libdrm-2.4.113.tar.xz解压: xz -d libdrm-2.4.113.tar.xz tar -xf libdrm-2.4.113.tar二、编译环境安装 1. 更新python ubuntu安…

CalBioreagents 艾美捷重组BCOADC-E2蛋白说明书

艾美捷CalBioreagents 重组BCOADC-E2蛋白英文说明&#xff1a; PRODUCT DESCRIPTION: Branched Chain 2-Oxo-Acid Dehydrogenase Complex E2 protein, recombinant. CLINICAL INDICATION: Primary biliary cirrhosis CATALOG NUMBER: A268 SOURCE: Recombinant protein ex…

《CTF攻防世界web题》之茶壶我爱你(2)

前言 &#x1f340;作者简介&#xff1a;被吉师散养、喜欢前端、学过后端、练过CTF、玩过DOS、不喜欢java的不知名学生。 &#x1f341;个人主页&#xff1a;被吉师散养的职业混子 &#x1fad2;文章目的&#xff1a;记录唯几我能做上的题 &#x1f342;相应专栏&#xff1a;CT…

Pytorch调用GPU时显示CUDA版本过低的解决方案

在调用torch.cuda.is_available时&#xff0c;有如下报错&#xff1a; cuda initialization: The Nvidia driver on your system is too old. 事情的发展是这样的&#xff1a; 1. 服务器的CUDA版本是10.1&#xff0c;仅支持pytorch版本最高1.7&#xff1b;前几天跑项目需要用…

Oracle 中常用的字符串函数总结

一、substr 函数 —— 字符串截取 格式1&#xff1a; substr(string,start_pos,length) 格式2&#xff1a; substr(string,start_pos) 说明&#xff1a; 从指定位置start_pos截取字符串string的length位&#xff0c;如果不指定length&#xff08;格式2&#xff09;则从指…

【数据挖掘】2022数据挖掘之Pandas库相关使用

数据挖掘之Pandas库相关使用一、概念1、介绍2、Pandas的优点3、软件推荐&#xff08;Jupyter Notebook&#xff09;4、软件下载网址以及参考文档二、基础知识1、DataFrame属性和方法1.1 结构&#xff08;1&#xff09;行索引&#xff08;2&#xff09;列索引1.2 常用属性&#…

配置非法AP设备检测和反制

1、业务需求 某企业分支机构为了保证工作人员可以随时随地访问公司网络资源&#xff0c;部署WLAN基本业务实现移动办公。且在覆盖区域内移动发生漫游时&#xff0c;不影响用户的业务使用。 分支机构位于开放式场所&#xff0c;容易受到网络入侵&#xff0c;例如攻击者在WLAN网络…

摒弃“短板”——数据中心基础设施运维管理建议书

数据中心是数字基础设施的重要组成部分&#xff0c;同时也是一整套复杂的设施。它不仅仅包括计算机系统和其它与之配套的设备&#xff08;例如通信和存储系统&#xff09;&#xff0c;还包含冗余的数据通信连接、环境控制系统、监控系统以及各种安全系统。运维管理又是数据中心…

基于智能优化算法实现自动泊车的路径动态规划(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 作为一种方便、快捷的交通工具&#xff0c;汽车已成为人们生活和工作的重要组成部分。随着汽车数量的逐年增加&#xff0c;有限…

在ubuntu上用QT写一个简单的C++小游戏(附源码)

最近老师让用Qt写一个可视化界面&#xff0c;然后就给了一个小视频&#xff0c;好奇的不得了&#xff0c;就照着做了一下 视频链接如下&#xff1a;C案例教学–一个类游戏小程序的设计与实现全过程–用到QT-简单的STL容器 创建项目 1、打开QT 如果不知道怎么下载的话&#xff…

【牛客网刷题】VL8-VL10 generate for语句、比较数大小、function的使用

&#x1f449; 写在前面 &#x1f449; 本系列博客记录牛客网刷题记录 &#x1f449; 日拱一卒&#xff0c;功不唐捐&#xff01; 目录 VL8 使用generate for语句简化代码 题目描述 输入描述 输出描述 RTL 设计 testbench 设计 仿真测试 VL9 使用子模块实现三输入数的大…

【C++ 程序设计入门基础】- Chapter One

目录 一、什么是 C&#xff1f; 1、概念 2、标准库 二、第一个 C 程序 1、下载 C 开发工具 2、开始下载好之后&#xff0c;我们先设置一下编码&#xff0c;解决中文注释不显示的问题。 3、下面我们就可以新建一个源代码 4、 编写完成后&#xff0c;我们就可以运行查看结果…

MyBatis:缓存机制详解

本篇内容包括&#xff1a;MyBatis 缓存机制概述、一级缓存与二级缓存的介绍、配置和具体流程。 一、MyBatis 缓存机制概述 在我们常见的 OLTP&#xff08;on-line transaction processing&#xff0c;联机事务处理&#xff09;类型的 Web 应用中&#xff0c;性能的瓶颈往往来源…