重新学树结构

news2024/12/27 13:48:35

图一
在这里插入图片描述

图二
在这里插入图片描述

相关术语

  • 前驱:某结点上一层结点,图中H结点的前驱结点是F
  • 后继:某结点紧跟的后面的结点,图中F结点的后继是G、H、I三个结点
  • 根结点:非空树没有前驱结点的结点,图中的R结点
  • 结点的度:结点拥有子树的个数,图中R结点度为3
  • 树的度:所有结点度的最大值,图中为3
  • 叶子结点(终端结点):子树(或者说度)数为0
  • 孩子结点:后继的了一种表示方法,G、H、I是F的孩子结点
  • 双亲结点:前驱结点的另一种表示方法,G、H、I的双亲结点是F
  • 兄弟结点:有共同的双亲结点,如G、H、I三个结点
  • 堂兄弟结点:同行(层)结点,如A、B、C三个结点
  • 结点的祖先:从根结点到某结点途中分支经过的所有结点,比如说H结点的祖先结点是F、C、R
  • 结点的子孙:从某结点往下所有分支的结点,比如说C结点的子孙结点是F、G、H、I
  • 树的深度(高度):最大层次,这里面是4
  • 有序树:树中结点的各子树从左至右是有序的,图二中三个子树,child1必须在左侧、child2必须在中间、child3必须在右侧
  • 森林:m棵(m是非负整数)互不相交的树(独立的树)的集合

二叉树

  • 最多两个分叉(即根结点下面只有左右子树)的树
  • 每个结点最多有两个子树
  • 有左右之分,不能颠倒

图三
在这里插入图片描述

性质

  • 第i行最多有pow(2, i-1)个结点,其中i是不小于1的整数
  • 深度为i的二叉树最多一共有pow(2, i)-1个结点
  • 叶子结点个数为k,度为2的结点个数为x,则k=x+1

满二叉树

  • 即满足二叉树性质二的二叉树

完全二叉树

  • 按我的理解是 : 把一个n层的满二叉树,把1~(2n-1)按照每层从左到右排序给结点排序,从第(2n-1)个结点(也就是对应满二叉树最右下角那个一个结点)连续去掉任意结点的树,都是完全二叉树,而且有个特点——除了根结点,左值都是偶数,右值都是奇数
  • 具有n个结点的完全二叉树的深度为int(log(2, n))+1

二叉树的存储结构

顺序存储结构:使用数组,按照满二叉树的结点层次编号,依次存放二叉树中的数据元素

  • 特点:空间浪费,只适用于满二叉树和完全二叉树的存储
#define MAXSIZE 100
typedef int TElemType;
typedef TElemType SqBiTree[MAXSIZE];

链式存储结构

二叉链表:每个结点包含三个字段:数据域、左子结点指针和右子结点指针。

  • 有n个结点二叉链表中,有n+1个空指针域
typedef struct BiNode {
    TElemType data;
    // 左右孩子指针
    struct BiNode * lchild, * rchild; 
} BiNode, *BiTree;

三叉链表:每个结点包含四个字段:数据域、左子结点指针、双亲结点指针和右子结点指针

typedef struct TriTNode {
    TElemType data;
    struct TriTNode *lchild, *parent, *rchild;
}TriTNode, *TriTree;

遍历二叉树的方法

在这里插入图片描述

  • 若规定先左后右遍历,L是左子树、D是根、R是右子树
  1. 先(根)序遍历(DLR)
  2. 中(根)序遍历(LDR)
  3. 后(根)序遍历(LRD)

如图

在这里插入图片描述

  • 如果采用先序遍历,得到:ABELDHMIJ
  • 如果采用中序遍历,得到:ELBAMHIDJ
  • 如果采用后序遍历,得到:LEBMIHJDA

使用三种遍历方式表示表达式

  • 表达式的前缀表示,又称波兰式
  • 表达式的中缀表示
  • 表达式的后缀表示,又称逆波兰式

由三种遍历方式的两两组合是否可以倒推二叉树

  • 先序遍历和中序遍历可以
  • 中序遍历和后序遍历可以
  • 先序遍历和后序遍历却不可以

三种递归遍历的复杂度

  • 时间复杂度:都是O(n)
  • 空间复杂度:最坏情况都是O(n)

二叉树的其他方法

深度

  • 如果是空树,返回0
  • 否则,分别递归计算左子树的深度为m,右子树的深度为n,返回二者最大的那个加上1

结点个数

  • 如果是空树,返回0
  • 否则,分别递归计算左子树的结点与右子树的结点m、n,求和加上1

叶子结点个数

  • 如果是空树,返回0
  • 否则,分别计算走左右子树的叶子结点个数,相加返回
  • 另外,如果左右子树为空,那么此时的根结点属于叶子结点

线索二叉树

  • 为了解决寻找特定遍历序列中二叉树结点的前驱和后继
  • 解决方法1:通过遍历寻找,但是费时间
  • 解决方法2:再添加前驱和后继指针域,但是增加了存储负担
  • 解决方法3:利用二叉链表的空指针域,如果左孩子为空,就把空的左孩子指针域指向其前驱;如果右孩子为空,就把右孩子指针域指向后继。这种改变指向的指针就称为线索

构造思路

  • 为了区分指针是指向孩子结点还是前驱后继,额外增添两个标志域来辨别,ltag和rtag
  • ltag为0说明是左指针指向左子树,是1说明左指针指向前驱结点
  • rtag为0说明是右指针指向右子树,是1说明右指针指向后继结点

相关代码

typedef struct ThreadedBiTreeNode{
    TElemType data;
    int ltag, rtag;
    struct ThreadedBiTree *lchild, *rchild;
} ThreadedBiTreeNode, *ThreadedBiTree;

树的存储结构

双亲表示法

  • 定义结构数组
  • 存放树的结点
  • 每个结点含有两个域:数据域和双亲域
  • 数据域:存放结点本身信息
  • 双亲域:指示本结点的双亲结点在数组中的位置

如图

在这里插入图片描述

特点

  • 找双亲容易,找孩子麻烦

相关代码

// 树的双亲表示
typedef struct TreeNode {
    // 当前索引对应结点的数据
    TElemType data;
    // 当前索引对应结点的双亲索引
    int parent;
} TreeNode;

#define MAX_TREE_SIZE 100
typedef struct {
    TreeNode treeNode[MAX_TREE_SIZE];
    // 根结点的索引位置与实际结点个数(因为最大个数不一定存满)
    int root, count;
} PTree;

孩子表示法

  • 把每个结点的孩子排列起来,看做成一个单链表,则n个结点有n个孩子链表(叶子的孩子链表为空)。而且n个头指针又组成了一个顺序表(含有n个元素的数组)存储

如图

在这里插入图片描述

特点

  • 找孩子容易,找双亲难

相关代码

// 树的孩子表示法
typedef struct CTNode {
    TElemType data; // 这个地方写成结点的索引也可以 int index;
    struct CTNode * child;
}CTNode;

#define MAX_NODE_SIZE 100
typedef struct {
    CTNode ctNode[MAX_NODE_SIZE];
    // 根结点的索引位置与实际结点个数(因为最大个数不一定存满)
    int root, count;
} CTree;

孩子兄弟表示法

  • 使用二叉链表作为树的存储结构,链表的每个结点的两个指针域分别指向其第一个孩子结点和下一个兄弟结点
  • 又称二叉树表示法

如图

在这里插入图片描述

特点

相关代码

// 树的孩子兄弟表示法
typedef struct CBNode{
    TElemType data;
    struct CBNode *firstChild, *nextSibling;
} CBNode;

树与二叉树的转换

  • 将树转化为二叉树进行处理,利用二叉树的算法进行对树的操作
  • 给定的一棵树,可以找到唯一的二叉树与之对应
  • 由于树与二叉树都可以用二叉链表(孩子兄弟存储结构)作为存储结构,则以二叉链表作为媒介可以实现树与二叉树的一一对应关系
  • 实质就是实现二者的互相解释器

树转换为二叉树

  • 兄弟直接加上连线
  • 对每个结点,除了其左孩子以外,去除其与其余孩子之间的关系
  • 以树的根结点为轴心,将整树顺时针旋转45°

二叉树转换为树

  • 若p结点是双亲结点的左孩子,则将p的右孩子,右孩子的右孩子……沿着分支找到所有的右孩子,都与p的双亲用线连起来
  • 抹掉原来二叉树中双亲与右孩子之间的连线
  • 将结点按层次排列,形成树结构

森林转换为二叉树

  • 将各棵树分别转换为二叉树
  • 将每棵树的根结点用线相连
  • 以第一颗树根结点作为二叉树的根,再以根结点为轴心,顺时针旋转,构成二叉树型结构

二叉树转换为森林

  • 将二叉树中根结点与其右孩子相连,以及右孩子的右孩子……的所有右孩子之间的连线全部抹掉,使之变成独立的二叉树(所有根结点与其所有右孩子的连线全部抹掉)
  • 然后将每个独立的二叉树还原成树

树的三种遍历方式

  • 先根遍历:若树不为空,则先访问根结点,然后依次先根遍历(递归)各棵子树
  • 后根遍历:若树不为空,则先依次后根遍历各棵子树,然后访问根结点
  • 按层次遍历,若树不为空,则从上到下,从左到右访问树中的各个结点

在这里插入图片描述

  • 先根遍历顺序:ABCDE
  • 后根遍历顺序:BDCEA
  • 层次遍历顺序:ABCED

森林的遍历

将森林看作三个部分组成:

  • 森林中第一棵树的根结点(第一部分)
  • 森林中第一棵树的子树森林(第二部分)
  • 森林中其它树构成的森林(第三部分)

森林的遍历方式

先序遍历:实质就是对从左到右的每棵树进行树的先根遍历

  • 访问森林的第一部分
  • 先序遍历(递归)森林中的第第二部分
  • 先序遍历森林中的第三部分

中序遍历:实质就是从左到右对每棵树进行树的后根遍历

  • 中序遍历森林中的第二部分

  • 访问森林中的第一部分

  • 中序遍历森林中的第三部分

  • 树的先序遍历顺序:ABCDEFGHIJ

  • 树的中序遍历顺序:BCDAFEHJIG

哈夫曼树(最优二叉树)

  • 给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。(摘自知乎文章,我觉得这种说法最适合我理解)
  • 老师给出的定义:树的带权路径长度最短的树

相关术语

  • 路径:从树中一个结点到另一个结点之间分支构成这两个结点的路径
  • 结点的路径长度:两个结点路径之间上的分支数
  • 树的路径长度:从根结点到每一个结点的路径之和。结点数目相同的二叉树中,完全二叉树的路径长度是最短的(这只是充分条件)
  • 权(权重):将树中结点赋值
  • 结点的带权路径长度:从根结点到某结点之间的路径长度与该结点的权的乘机
  • 树的带权路径长度:树当中所有叶子结点的带权路径长度之和
  • 哈夫曼算法:构造哈夫曼树的算法

特点

  • 满二叉树不一定是哈夫曼树
  • 哈夫曼树中权越大的叶子离根越近
  • 具有相同的带权结点的哈夫曼树不唯一
  • 哈夫曼树中结点的度只有0或者2,没有度为1的结点
  • 包含n个叶子结点的哈夫曼树中共有2n-1个结点

哈夫曼树的构造

  • 根据n个给定的权值{W1, W2, W3, …, Wn},构成n棵二叉树组成的森林F,每个二叉树只有一个根结点,权重为Wi
  • 在F中选取两个根结点权值最小的二叉树作为左右子树,构造一棵新的二叉树,且设置新的二叉树的根结点的权值为左右子树根结点的权值之和
  • 在F中删除这两个树(就是上一步用于构成新的二叉树的左右子树),同时将新得到的二叉树添加到森林中
  • 重复第二步与第三步,直到森林中只剩下一棵树,就是哈夫曼树

哈夫曼算法的实现

  • 采用顺序存储——一维结构数组
  • 结构如下
typedef struct {
    // 假设权重取整数值
    int weight;
    // 分别是双亲结点、左孩子、右孩子的索引值,-1代表没有
    int parent, lchild, rchild;
} HTNode, *HuffmanTree;

算法代码

// 初始化哈夫曼树,传入叶子结点的值组成的数组和叶子结点个数n(至少是2)
HuffmanTree createHT(const int weights[], int n){
    HuffmanTree huffmanTree = (HTNode *) malloc((n * 2-1) * sizeof (HTNode));
    for (int i=0;i<(2*n-1);i++){
        huffmanTree[i].lchild = -1;
        huffmanTree[i].rchild = -1;
        huffmanTree[i].parent - -1;
    }
    for (int j=0;j<n;j++) huffmanTree[j].weight = weights[j];
    return huffmanTree;
}

// 销毁哈夫曼树
void destroyHT(HuffmanTree huffmanTree){
    free(huffmanTree);
}

// 根据哈夫曼算法实现哈夫曼树
HuffmanTree realizeHT(HuffmanTree huffmanTree, int n){
    // 进行n-1次合并
    // 每次在当前森林里面的二叉树中找到两个根结点权值最小的
    // 在剩下的n-1个顺序表中的结点逐个补全,这n-1个结点组成的子顺序表就是哈夫曼树
    for (int i=n;i<2 * n-1;i++){
        // 在huffmanTree[k](0<= k <= i)中选择两个其双亲域为-1,而且权值最小的结点,并返回它们在huffmanTree中的序号s1和s2
        Select(huffmanTree, i-1, s1, s2);
        // 表示从森林中删除s1,s2
        huffmanTree[s1].parent = i;
        huffmanTree[s2].parent = i;
        // s1,s2分别作为新二叉树的左右孩子
        huffmanTree[i].lchild = s1;
        huffmanTree[i].rchild = s2;
        huffmanTree[i].weight = huffmanTree[s1].weight + huffmanTree[s2].weight;
    }
}

哈夫曼编码

  • 是一种无损压缩算法,通过对不同的符号赋予不同的变长编码,使得频率较高的符号具有较短的编码,而出现频率低的符号具有较长的编码,从而实现对数据进行高效压缩。

特点

  • 统计每个字符在电文中的出现的平均概率,概率越大,要求编码越短
  • 把每个字符的概率值作为权值,构造哈夫曼树,概率越大的结点,路径越短
  • 在哈夫曼树的每个分支上标上0或者1,结点的左分支标0,右分支标1;把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码
  • 哈夫曼编码可以保证是前缀编码(一种不会产生歧义的编码),因为没有一片树叶是另一树叶的祖先,所以每个叶结点的编码就不可能是其它叶结点编码的前缀(就是一个编码不可能是另一个编码的某片段截取)
  • 能保证字符编码的总长度最短。哈夫曼树本身就是带权路径最短的树,所以编码的总长度最短

声明:
部分图片源于互联网、部分更通俗易懂的概念源于搜索
教学看的是青岛大学王卓老师的
源代码下载:https://gitee.com/PythonnotJava/Data-structure-and-algorithm-collection/tree/master/ReTree

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

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

相关文章

019+limou+C语言预处理

0.前言 您好&#xff0c;这里是limou3434的一篇博客&#xff0c;感兴趣您可以看看我的其他博文系列。本次我主要给您带来了C语言有关预处理的知识。 1.宏的深度理解与使用 1.1.数值宏常量 #define PI 3.1415926注意define和#之间是可以留有空格的 1.2.字符宏常量 #includ…

设置论文中的图、表的题注

参考b站&#xff1a;毕业论文图表如何自动编号/word图表自动编号/图表编号自动更新 其中&#xff0c;更新图表序号 视频使用ctrl 设置论文中的图、表的题注 step1:设置章节1.1: 章节设置字体样式&#xff0c;选择标题11.2&#xff1a;章节添加序号1.3 修改序号 和字之间的缩进&…

Linux->线程基本概念

目录 前言&#xff1a; 1. 线程的基本概念 2 线程的优点 3 线程的缺点 4 数据块大小为4KB大小的真正原因 前言&#xff1a; 本篇文章讲解了线程与进程之间的区别和联系&#xff0c;线程的优缺点&#xff0c;还有内存的数据管理与磁盘之间的关系&#xff0c;虚拟内存到内存…

阿里云服务器提供哪些操作系统和软件支持?是否与常用软件兼容?

阿里云服务器提供哪些操作系统和软件支持&#xff1f;是否与常用软件兼容&#xff1f;    阿里云服务器支持的操作系统   为了满足不同用户需求&#xff0c;阿里云服务器&#xff08;ECS&#xff09;提供了丰富的操作系统选择。以下是阿里云服务器支持的主要操作系统&#…

Linux 配置MySQL环境(三)

Linux配置MySQL环境 一、下载1. 官网下载MySQL2. 百度网盘快速下载MySQL 二、安装1、通过 Xftp 将 MySQL 安装包拷贝到 Linux2、解压缩3、安装 common、libs、client、server4、初步连接 三、卸载四、常用设置1. 修改 root 用户密码 五、使用新密码登录六、开启远程访问七、开放…

PHP设计模式21-工厂模式的讲解及应用

文章目录 前言基础知识简单工厂模式工厂方法模式抽象工厂模式 详解工厂模式普通的实现更加优雅的实现 总结 前言 本文已收录于PHP全栈系列专栏&#xff1a;PHP快速入门与实战 学会好设计模式&#xff0c;能够对我们的技术水平得到非常大的提升。同时也会让我们的代码写的非常…

OpenCV 笔记_5

文章目录 笔记_5特征点匹配DMatch 存放匹配结果DescriptorMatcher::match 特征点描述子&#xff08;一对一&#xff09;匹配DescriptorMatcher::knnMatch 特征点描述子&#xff08;一对多&#xff09;匹配DescriptorMatcher::radiusMatch 特征点描述子&#xff08;一对多&#…

Frontiers in Microbiology:DAP-seq技术在猪苓C2H2转录因子PuCRZ1调控菌丝生长及渗透胁迫耐受性机制研究中的应用

猪苓&#xff08;Polyporus umbellatus&#xff09;是一种可食用的蘑菇&#xff0c;也是我国常用的菌类药材之一&#xff0c;至今已有2000多年的药用历史&#xff0c;在《神农本草经》、《本草纲目》、《本草求真》等典籍中均有记载。猪苓具有利尿、抗菌作用&#xff0c;近年来…

SpringBatch从入门到实战(二):HelloWorld

一&#xff1a;HelloWorld 1.1 配置Job、Step、Tasklet Configuration public class HelloWorldJobConfig {Autowiredprivate JobBuilderFactory jobBuilderFactory;Autowiredprivate StepBuilderFactory stepBuilderFactory;Beanpublic Job helloWorldJob() {return jobBuild…

代码随想录算法训练营第五十九天|503.下一个更大元素II 42. 接雨水

目录 LeeCode 503.下一个更大元素II LeeCode 42. 接雨水 暴力解法 优化双指针法 单调栈法 LeeCode 503.下一个更大元素II 503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 【思路】 相较于前两道题目&#xff0c;这道题目将数组改为循环数组&#x…

python获取度娘热搜数据并保存成Excel

python获取百度热搜数据 一、获取目标、准备工作二、开始编码三、总结 一、获取目标、准备工作 1、获取目标&#xff1a; 本次获取教程目标&#xff1a;某度热搜 2、准备工作 环境python3.xrequestspandas requests跟pandas为本次教程所需的库&#xff0c;requests用于模拟h…

在读博士怎么申请公派访学?

作为在读博士生&#xff0c;申请公派访学是一项重要而有益的经历。下面知识人网将为您介绍一些关于如何申请公派访学的步骤和注意事项。 首先&#xff0c;您需要找到一个合适的公派访学机会。可以通过与导师、教授或其他相关人士进行交流来获取相关信息。还可以参考学术会议、研…

【Linux】linux | 服务响应慢、问题排查 | 带宽问题导致

一、说明 1、项目使用云服务器&#xff0c;服务器配置&#xff1a;5M带宽、4核、32G&#xff0c;1T&#xff0c;CentOS7 2、CPU、内存、磁盘IO都没有达到瓶颈&#xff0c;猜测是带宽问题 3、应用比较多&#xff0c;应用中间件&#xff0c;十几个差不多 4、同时在线人数30 5、已…

继承~~~

1&#xff1a;继承概述&#xff0c;使用继承的好处 1&#xff1a;什么是继承&#xff1f; Java中提供一个关键字extends&#xff0c;用这个关键字&#xff0c;我们可以让一个类和另一类建立起父子关系。 public class Student extends People{} Student称为子类&#xff08…

乘势而起:机载航电·显控显示系统仿真

改革开放以来&#xff0c;我国国民经济与科学技术取得了长足的发展&#xff0c;信息化、工业成熟度与自动化程度不断深化&#xff0c;极大地增强了国家的综合实力、在世界范围内显示了大国地位。在当前科技产业的发展和变革的历史性交汇期&#xff0c;“工业4.0”、“中国制造2…

iOS App 上架流程图文教学

在上架App 之前必须先准备好开发者帐号&#xff0c;但申请开发者帐号因法兰克早在之前已经申请好了&#xff0c;故就跳过此步骤&#xff0c;直接从产生凭证到上传App开始讲起。首先&#xff0c;要将自己辛苦写好的App 送审的话&#xff0c;则要依序做完下列几件事情即可。 在开…

NFT Insider #98:The Sandbox与Forkast Labs 达成合作,苹果的混合现实头显将增强游戏和元宇宙

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members(https://twitter.com/WHALEMembers)、BeepCrypto&#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周…

对称加密DES加密算法原理、用户登录应用案例分享

更多知识 学习&#xff1a;https://www.processon.com/view/60504b5ff346fb348a93b4fa#map 目录&#xff1a; DES算法的工作原理DES算法的优点DES算法的缺点DES算法的应用场景DES算法性能分析用户登录实例总结 1. DES算法的工作原理 DES算法是一种对称加密算法&#xff0c;使…

定向写作模型CTRL,Conditional Transformer Language有条件的文本生成模型

定向写作模型CTRL 介绍 CTRL全称为Conditional Transformer Language有条件的文本生成模型&#xff0c;它始于Salesforce在2019年发布的论文《A Conditional Transformer Language Model for Controllable Generation》&#xff0c;该模型用于定向写作。论文地址如下&#xff…

双功能螯合剂:1777804-45-1,DOTA-(COOt-Bu)3-CH2-Ph-azide,的反应特点及其参数说明

​ 中文名称&#xff1a;1,1-二甲基乙基4-[(4-叠氮苯基)甲基] -7,10-双[2-(1,1-二甲基乙氧基) -2-氧乙基] -1,4,7,10-四氮杂环十二烷 -1-乙酸酯 英文名称&#xff1a;DOTA-(COOt-Bu)3-CH2-Ph-azide&#xff0c;DOTA-(COOt-Bu)3-CH2-Ph-N3 规格标准&#xff1a;1g、5g、10g CAS…