数据结构(八)----树

news2024/12/26 22:57:05

目录

一.树的逻辑结构

1.双亲表示法(顺序存储)

2.孩子表示法(顺序+链式存储)

3.孩子兄弟表示法(链式存储)

二.树的遍历

1.先根遍历

2.后根遍历

3.层次遍历

三.森林的遍历

1.森林的先序遍历

2.森林的中序遍历

四.哈夫曼树

1.带权路径长度

2.构造哈夫曼树

3.哈夫曼编码


一.树的逻辑结构

树是n (n\geq0) 个结点的有限集合,n=0时,称为空树,这是一种特殊情况。

在任意一棵非空树中应满足:

1)有且仅有一个特定的称为根的结点。

2)当n>1时,其余结点可分为m (m>0) 个互不相交的有限集合T1,T2.…. Tm,其中每个集合本身又是一棵树,并且称为根结点的子树。

1.双亲表示法(顺序存储)

除了根节点之外,其它节点具有唯一的父节点,所以每个节点除了保存数据外,还会保存指向双亲的“指针”。

如下图所示,-1表示没有双亲,B,C,D的双亲为0号节点。

注:新增数据元素,无需按逻辑上的次序存储

#define MAX_TREE_SIZE 100    //树中最多结点数
typedef struct{    //树的结点定义
    ElemType data;    //数据元素
    int parent;    //双亲位置域
}pTNode;

typedef struct{     //树的类型定义
    PTNodenodes[MAX_TREE_SIZE];    //双亲表示 
    int n;    //结点数
}PTree;

•插入新节点:写入新节点的值,并记录其与双亲的关系。

 注:新增数据元素,无需按逻辑上的次序存储

•删除节点:

方案一:删除元素后,将其双亲"指针"设为"-1"。

方案二:将尾部的数据移动到删除元素的位置。

哪个方案更好呢?

若要删除的节点不是叶子节点,那么需要将其子孙节点全部删除,这就需要依次遍历,查找他的孩子节点。若采用第一种方案,则遍历时会出现空数据,会导致遍历速度下降。

在二叉树的顺序存储中,二叉树的节点编号与完全二叉树对应:

i的左孩子---2i

i的右孩子---2i+1

i的父节点---\left \lfloor i/2 \right \rfloor

可以看到,节点的编号不仅反映了存储位置,也隐含了节点之间的逻辑关系。

忘记了可以看看:二叉树

2.孩子表示法(顺序+链式存储)

顺序存储各个节点,每个结点除了保存数据域外,还保存了指向第一个孩子的指针。

struct CTNode {
    int child;              // 孩子结点在数组中的位置
    struct CTNode *next;    // 下一个孩子
};
typedef struct {
    ElemType data;      
    struct CTNode *firstChild;    // 第一个孩子
} CTBox;
typedef struct {
    CTBox nodes[MAX_TREE_SIZE];
    int n,r;               // 结点数和根的位置
} CTree;
3.孩子兄弟表示法(链式存储)
typedef struct csNode{
    ElemType data;     //数据域
    struct CSNode *firstchild,*nextsibling;   
    //firstchild指向第一个孩子,nextsibling指向第一个孩子的右兄弟
}CSNode,*CSTree; 

如下图所示:

A的第一个孩子是B,A的左指针指向B,B的右兄弟为C,所以B的右指针指向C,C的右兄弟是D,所以C的右指针指向D,其他依此类推。

这就实现了树到二叉树的转化。

二叉树到树转换的逻辑与上面反过来就行:

补充:森林和二叉树的相互转换

1.首先将森林里的每一棵树转换为二叉树:

2.将根节点看作兄弟结点,通过右指针指向:

二叉树转换为森林的逻辑反过来即可:

二.树的遍历

1.先根遍历

树的先根遍历与二叉树的先根遍历原理相同。

将其对应的二叉树画出来,可以观察到,树的先根遍历序列与这棵树相应二叉树的先序序列相同。

2.后根遍历

转换为对应二叉树后,树的后根遍历序列与这棵树相应二叉树的中序序列相同。

3.层次遍历

与二叉树的层次遍历相同:

① 若树非空,则根节点入队

② 若队列非空,队头元素出队并访问,同时将该元素的孩子依次入队

③ 重复②直到队列为空

注:树的层次遍历可以称为广度优先遍历;相对地,先根遍历和后根遍历可以称为深度优先遍历。

三.森林的遍历

1.森林的先序遍历

若森林为非空,则按如下规则进行遍历:

1.访问森林中第一棵树的根结点。

2.先序遍历第一棵树中根结点的子树森林。

3.先序遍历除去第一棵树之后剩余的树构成的森林。

如下图所示,① 先访问B,在访问第一棵树中根节点的子树森林,即以E,F为根节点的森林。② 递归到第一步,访问子树森林中第一棵树的根节点,即E,再先序遍历E的两棵子树森林K,L。③ 先序遍历除去第一棵树之后剩余的树构成的森林,即F。

其等同于依次对各个树进行先根遍历,最后结果如下:

或者可以将森林先转换为二叉树,森林的先序遍历序列和二叉树的先序遍历序列也是相同的。

2.森林的中序遍历

1.中序遍历森林中第一棵树的根结点的子树森林。

2.访问第一棵树的根结点。

3.中序遍历除去第一棵树之后剩余的树构成的森林。

对森林的中序遍历,效果等同于依次对各个树进行后根遍历,结果如下:

或者可以把他转化为对应的二叉树,森林的中序遍历,效果等同于对应二叉树的中序遍历

总结:
森林先序遍历效果等同于森林中各个树先根遍历,也等同于对二叉树先序遍历

森林中序遍历效果等同于森林中各个树后根遍历,也等同于对二叉树中序遍历

四.哈夫曼树

1.带权路径长度

结点的权:有某种现实含义的数值(如:表示结点的重要性等),下图所示,每一个结点都有对应的权值。

结点的带权路径长度:从树的根到该结点的路径长度(经过的边数)与该结点上权值的乘积。

例如,权值为3的结点:从根结点到权值为3的结点的路径长度为3,所以其带权路径长度为3*3=9.

树的带权路径长度:树中所有叶结点的带权路径长度之和(WPL,Weighted Path Length)

注:树的路径长度是指根到每个结点的路径长的总和,根到每个结点的路径长度的最大值应是树的高度-1,注意与这里做区别。

例如:

第一个图中,叶子结点有4个,根到各个结点的路径长度是2,所以这棵树的带权路径长度:

2*1+2*3+2*4+2*5=26

第二个图中,叶子结点有4个,根到1,3,4,5的路径长度为3,3,2,1,所以带权路径长度:1*5+2*4+3*1+3*3=25

第三个图同理,带权路径长度为:1*5+2*4+3*1+3*3=25

第四个图的带权路径长度为:1*1+2*3+3*5+3*4=34

哈夫曼树:在含有n个带权叶结点的二叉树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称最优二叉树。上面计算的中间两棵树都是哈夫曼树。

2.构造哈夫曼树

(1) 将这n个结点分别作为n棵仅含一个结点的二叉树,构成森林F。

(2) 构造一个新结点,从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值置为左、右子树上根结点的权值之和。

(3) 从F中删除刚才选出的两棵树,同时将新得到的树加入F中。(当然也可以选择e和b)

(4) 重复步骤2)和3),直至F中只剩下一棵树为止。

这棵树的带权路径长度WPL_{min}=1*7+2*3+3*2+4*1+4*2=31

从上面可以观察到:

• 每个初始结点最终都成为叶结点,且权值越小的结点到根结点的路径长度越大。

•n个结点,两两结合为一棵树,总共需要合并n-1次,每一次合并都会增加一个结点,所以哈夫曼树的结点总数为n+n-1=2n-1个。

•哈夫曼树中不存在度为1的结点。

•哈夫曼树并不唯一,但WPL必然相同且为最优。

3.哈夫曼编码

对于固定长度编码,每个字符需要用相等长度的二进制位表示,例如:

若用权值表示各个字母的使用次数,假设A的使用次数:10,B:8,C:80,D:2,那么其带权路径长度可以这样表示:从根节点出发,向左的路径是二进制0,向右的路径是二进制1(也可以反过来)

上面这棵树的带权路径长度为200,用A,B,C,D构造哈夫曼树,能使带权路径长度达到最小:

上面这棵树对应的A,B,C,D的编码如下:C使用了最多次,所以C的编码最简单,以这样的逻辑进行编码,那么发送的编码长度就能最小化。

这种编码方式就是可变长度编码,允许对不同字符用不等长的二进制位表示。

A的使用次数也很高,为什么不能直接将其编码为1呢?也就是A不作为叶子节点

采用这样的编码方式,若要发送CAAABD这样的字符,则发送的二进制编码为:

0 1 1 1 111 110

接收者接收到这一串二进制编码时,可能翻译为:

导致解码错误,产生歧义。

所以A,B,C,D字符只能作为叶子节点,不能当作分支节点。

上面这样的编码也可称为前缀编码,也就是没有一个编码是另一个编码的前缀。采用这种前缀码,解码时才不会产生歧义。

上图,由于A的编码是1,与B,D有相同的前缀,所以会产生解码歧义。

注:之前讲过,对于给定的叶子节点,哈夫曼树是不唯一的,所以哈夫曼编码也是不唯一的。

由于哈夫曼编码能使发送的数据长度达到最小,所以哈夫曼编码也用于数据的压缩

上图中哈夫曼编码的长度为130,加权平均长度计算得到的WPL与各编码的权值之和的比值:130/80+10+2+8=13/10

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

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

相关文章

Flutter创建自定义的软键盘

参考代码: Flutter - Create Custom Keyboard Examples 本文贴出的代码实现了一个输入十六进制数据的键盘: (1)支持长按退格键连续删除字符; (2)可通过退格键删除选中的文字; &…

《HelloGitHub》第 97 期

兴趣是最好的老师,HelloGitHub 让你对编程感兴趣! 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等,涵盖多种编程语言 Python、…

护航智慧交通安全 | 聚铭精彩亮相2024交通科技创新及信创产品推广交流会

4月26日,石家庄希尔顿酒店内,河北省智能交通协会盛大举办2024年度交通科技创新及信创产品推广交流会。聚铭网络受邀参与,携旗下安全产品及解决方案精彩亮相,为智慧交通安全保驾护航。 为深化高速公路创新驱动发展战略&#xff0…

CUDA架构介绍与设计模式解析

文章目录 **CUDA**架构介绍与设计模式解析**1** CUDA 介绍CUDA发展历程CUDA主要特性CUDA系统架构CUDA应用场景编程语言支持CUDA计算过程线程层次存储层次 **2** CUDA 系统架构分层架构并行计算模式生产-消费者模式工作池模式异步编程模式 **3** CUDA 中的设计模式工厂模式策略模…

ChatGPT理论分析

ChatGPT "ChatGPT"是一个基于GPT(Generative Pre-trained Transformer)架构的对话系统。GPT 是一个由OpenAI 开发的自然语言处理(NLP)模型,它使用深度学习来生成文本。以下是对ChatGPT进行理论分析的几个主…

用户中心(上)

文章目录 企业做项目流程需求分析技术选型计划初始化项目数据库设计登录/注册⽤户管理(仅管理员可⻅) 初始化项目⼀、前端初始化1.下载nodejs2.Ant Design Pro相关问题1.前端项目启动时报错、无法启动?2.如何初始化前端项目?为什么…

SpringCloud之OpenFeign

学习笔记: 官网地址:https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/#spring-cloud-feign 源码:https://github.com/spring-cloud/spring-cloud-openfeign 1、概念总结 OpenFeign是一个声明式的Web服务客户端…

[python数据处理系列] 深入理解与实践基于聚类的过采样与欠采样技术:以K-Means为例

目录 一、过采样介绍 (一)什么是过采样 (二)过采样的优点 (三)过采样的缺点 二、欠采样介绍 (一)什么是欠采样 (二)欠采样的优点 (三)欠采样的缺点 三、基于聚类的欠抽样方法(K-Means欠采样/KMeans-Undersampling) (一)KMeans欠采样原理及其步骤介绍 (二)为什么不采…

上海亚商投顾:沪指创年内新高 房地产板块掀涨停潮

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 三大指数昨日继续反弹,沪指盘中涨超1%,重返3100点上方,深成指涨超2%&#…

ArcGIS小技巧—坐标系匹配

坐标系:(Coordinate System):在一些书籍和软件中也叫做空间参考,简单来说,有了坐标系,我们才能够用一个或多个“坐标值”来表达和确定空间位置。没有坐标系,坐标值就无从谈起&#x…

IP定位技术企业网络安全检测

随着信息技术的飞速发展,网络安全问题日益凸显,成为企业运营中不可忽视的一环。在众多网络安全技术中,IP定位技术以其独特的优势,为企业网络安全检测提供了强有力的支持。本文将深入探讨IP定位技术在企业网络安全检测中的应用及其…

在idea中连接mysql

IDE(集成开发环境)是一种软件应用程序,它为开发者提供编程语言的开发环境,通常集成了编码、编译、调试和运行程序的多种功能。一个好的IDE可以大幅提高开发效率,尤其是在进行大型项目开发时。IDE通常包括以下几个核心组…

Excel 批量获取sheet页名称,并创建超链接指向对应sheet页

参考资料 用GET.WORKBOOK函数实现excel批量生成带超链接目录且自动更新 目录 一. 需求二. 名称管理器 → 自定义获取sheet页名称函数三. 配合Index函数,获取所有的sheet页名称四. 添加超链接,指向对应的sheet页 一. 需求 ⏹有如下Excel表,需…

(三十一)第 5 章 数组和广义表(稀疏矩阵的三元组行逻辑链接的顺序存储表示实现)

1. 背景说明 2. 示例代码 1)errorRecord.h // 记录错误宏定义头文件#ifndef ERROR_RECORD_H #define ERROR_RECORD_H#include <stdio.h> #include <string.h> #include <stdint.h>// 从文件路径中提取文件名 #define FILE_NAME(X) strrchr(X, \\) ? strrch…

Servlet(三个核心API介绍以及错误排查)【二】

文章目录 一、三个核心API1.1 HttpServlet【1】地位【2】方法 1.2 HttpServletRequest【1】地位【2】方法【3】关于构造请求 1.3 HttpServletResponse【1】地位【2】方法 四、涉及状态码的错误排查&#xff08;404……&#xff09;五、关于自定义数据 ---- body或query String …

Linux网络抓包工具tcpdump是如何实现抓包的,在哪个位置抓包的?

Linux网络抓包工具tcpdump是如何实现抓包的&#xff0c;在哪个位置抓包的&#xff1f; 1. tcpdump抓包架构2. BPF介绍3. 从内核层面看tcpdump抓包流程3.1. 创建socket套接字3.2. 挂载BPF程序 4. 网络收包抓取5. 网络发包抓取6. 疑问和思考6.1 tcpdump抓包跟网卡、内核之间的顺序…

ffmpeg音视频裁剪

音视频裁剪&#xff0c;通常会依据时间轴为基准&#xff0c;从某个起始点到终止点的音视频截取出来&#xff0c;当然音视频文件中存在多路流&#xff0c;所对每一组流进行裁剪 基础概念&#xff1a; 编码帧的分类&#xff1a; I帧(Intra coded frames): 关键帧&#xff0c;…

linux(ubuntu18.04.2) Qt编译 MySQL(8.0以上版本)链接库 Qt版本 5.12.12及以上 包含Mysql动态库缺失问题

整理这篇文档的意义在于&#xff1a;自己走了很多弯路&#xff0c;淋过雨所以想为别人撑伞&#xff0c;也方便回顾&#xff0c;仅供参考 一、搭建开发环境&#xff1a; 虚拟机&#xff08;ubuntu-20.04.6-desktop-amd64&#xff09;&#xff1a;Mysql数据库 8.0.36Workbench …

pytorch库 01 安装Anaconda、Jupyter,Anaconda虚拟环境连接pycharm

文章目录 一、安装Anaconda1、卸载Anaconda&#xff08;可选&#xff09;2、下载并安装Anaconda3、配置环境变量4、桌面快捷方式 二、安装 PyTorch&#xff08;GPU 版&#xff09;库1、创建虚拟环境&#xff0c;并安装一些常用包2、GPU 基础3、检查驱动4、安装CUDA&#xff08;…

数字化转型新篇章:企业通往智能化的新范式

早在十多年前&#xff0c;一些具有前瞻视野的企业以实现“数字化”为目标启动转型实践。但时至今日&#xff0c;可以说尚无几家企业能够在真正意义上实现“数字化”。 在实现“数字化”的征途上&#xff0c;人们发现&#xff0c;努力愈进&#xff0c;仿佛终点愈远。究其原因&a…