数据结构——哈夫曼树及其应用(哈夫曼编码)

判断树:用来描述分类过程的二叉树

哈夫曼树(最优二叉树)的基本概念

路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。

结点的路径长度:两结点间路径上的分支数

结点的路径长度计算:

 树的路径长度:从树根到每一个结点的路径长度之和。记作TL

示例:

 结点数目相同的二叉树中,完全二叉树是路径长度最短的二叉树。

权(weight):将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权

 结点的带权路径长度:

从根结点到该结点之间的路径长度与该结点的权的乘积。

树的带权路径长度:树中所有叶子结点的带权路径长度之和。(要与树的路径长度区分开来,树的路径长度是所有结点的路径长度之和,而树的带权路径长度则是所有叶子结点的带权路径长度之和)

 可知,相同的叶子结点数,相同的权值,数的形态不同,树的带权路径长度是不一样的。

 哈夫曼树:最优树【带权路径长度(WPL)最短的树】

注:“带权路径长度最短”是在“度相同”的树中比较而得的结果,因此有最优二叉树、最优三叉树之称等等。

最优二叉树

带权路径长度最短的二叉树。构造哈夫曼树的算法称为哈夫曼算法。

满二叉树不一定是哈夫曼树

由上图可以看出,权值比较小的结点离根结点比较远,权值比较大的离根结点比较近,具有相同带权结点的哈夫曼树不唯一。

构造哈夫曼树(哈夫曼算法)

(1)根据n个给定的权值{W1,W2,...,Wn}构成n棵二叉树的森林,F={T1,T2,...,Tn},其中Ti只有一个带权为wi的根结点。

  • 构造森林全是根

(2)在F中选取两棵结点的权值最小的树作为左右子树,构造一棵新的二叉树,且设置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。

  • 选用两小造新树

(3)在F中删除这两棵树,同时将新得到的二叉树加入到森林中。

  • 删除两小添新人

(4)重复(2)和(3),直到森林中只有一棵树为止,这棵树即为哈夫曼树。

  • 重复2、3剩单根

哈夫曼树的特点是,权重越大的节结点位于离根结点越近的位置,哈夫曼树的结点的度数为0或2,没有度为1的结点;包含n个n叶子结点的哈夫曼树中共有2n-1个结点(包含n棵树的森林要经过n-1次合并才能形成哈夫曼树,共产生n-1个新结点)。

哈夫曼树构造算法的实现

采用顺序存储结构——一维结构数组 HuffmanTree H;(叶子结点没有左右孩子,根结点没有双亲)

 结点类型定义

typedef struct {int weight;int parent, lch, rch;
}HTNode,*HuffmanTree;

例如:第一个结点权值为5,即可表示为H[i].weight = 5;

例如:有n=8,权值为W={7,19,2,6,32,3,21,10},构造哈夫曼树

1、初始化HT[1......2n-1]:lch=rch=parent=0

2、输入初始n个叶子结点:置HT[1.......n]的weight值; 

代码实现:

void CreateHuffmanTree(HuffmanTree HT, int n)
{if (n <= 1)return;m = 2 * n - 1;//数组共2n-1个元素HT = new HTNode[m + 1];//0号单元未用,HT[m]表示根结点for (i = 0; i < m; ++i)//将2n-1个元素的lch、rch、parent置为0{HT[i].lch = 0;HT[i].rch = 0;HT[i].parent = 0;}for (i = 1; i <= n; i++)cin >> HT[i].weight;//输入前n个元素的weight值//初始化结束,下面开始建立哈夫曼树
}

 3、进行以下n-1次合并,依次产生n-1个结点HT[i],i=n+1,......,2n-1;

a)在HT[1..i-1]中选两个未被选过(从parent ==0 的结点中选)的weight最小的两个结点HT[s1]和HT[s2],s1、s2为两个最小结点下标

b)修改HT[s1]和HT[s2]的parent值:HT[s1].parent=i; HT[s2].parent=i

c)修改新产生的HT[i]:

  • HT[i].weight=HT[s1].weight + HT[s2].weight;
  • HT[i].lch=s1; HTi. rch=s2;

代码实现:

for (i = n + 1; i <= m; i++)//合并产生n-1个结点——构造哈夫曼树(Huffman树)
{Select(HT,i-1,s1,s2);//在HT[k](1<=k<=i-1)中选择两个双亲域为0,//且权值最小的结点,并返回他们在HT中的序号s1和s2HT[s1].parent = i; HT[s2].parent = i;//表示从F中删除s1,s2HT[i].lch = s1; HT[i].rch = s2;//s1,s2分别作为i的左右孩子HT[i].weight = HT[s1].weight + HT[s2].weight;//i的权值为左右孩子权值之和
}

什么是哈夫曼编码

若将编码设计为长度不等的二进制编码,即让待传字符串中出现次数较多的字符采用尽可能短的编码,则转换的二进制字符串便可能减少。 

 

 关键:要设计长度不等的编码,则必须使任一字符的编码都不是另一个字符的编码的前缀。

 (这种编码称为前缀编码

问题:什么样的前缀编码能使得电文总长最短?

        ——哈夫曼编码

1、统计字符集中每个字符在电文中出现的平均概率(概率越大,要求编码越短)。

2、利用哈夫曼树的特点:权越大的叶子离根越近;将每个字符的概率值作为权值,构造哈夫曼树。则概率越大的结点,路径越短。

3、在哈夫曼树的每个分支上标上0或1:结点的左分支标 0,右分支标1,把从根到每个叶子的路径上的标号连接起来,作为该叶子代表的字符的编码。

【例 】 要传输的字符集 D = {C,A,S,T,;}

        字符出现频率w={2,4,2,3,3 }

构造哈夫曼树,如下:

 左分支标记0,右分支标记1,如下:

从根结点出发,路过的分支连起来,作为字符的编码,这个编码就叫做哈夫曼编码,如下:

 

 例电文是{CAS;CAT:SAT;AT}

其编码是:11010111011101000011111000011000

反之,若编码是1101000,则电文为CAT

 问题:

1、为什么哈夫曼编码能够保证是前缀编码?

因为没有一片树叶是另一片树叶的祖先,所以每一个叶子结点的编码就不可能是其他叶子结点的前缀

2、为什么哈夫曼编码能够保证字符编码总长最短?

因为哈夫曼树的带权路径长度最短,故字符编码的总长最短。

  • 性质1:哈夫曼编码是前缀码
  • 性质2:哈夫曼编码是最优前缀码

哈夫曼编码的算法实现

void CreateHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n) {//从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中HC = new char* [n + 1]; //分配n个字符编码的头指针矢量cd = new char[n];//分配临时存放编码的动态数组空间cd[n - 1] = '\0';//编码结束符for (i = 1; i <= n; ++i) {start = n - 1; c = i; f = HT[i].parent;while (f != 0) {//从叶子结点开始向上溯源,知道根结点,没有双亲--start;  //回溯一次start向前指一个位置if (HT[f].lchild == c)cd[start] = '0';//结点c是f的左孩子,则生成代码0else                  cd[start] = '1';//结点c是f的右孩子,则生成代码1c = f; f = HT[f].parent;//继续向上溯源}HC[i] = new char[n - start];//为第i个字符串编码分配空间strcpy(HC[i], &cd[start]);//将求得的编码从临时空间cd分配到HC的当前行中}delete cd; //释放临时空间
}

文件的编码和解码

1.编码:

  • 输入各字符及其权值
  • 构造哈夫曼树--HT[i]
  • 进行哈夫曼编码--HC[i]
  • 查HC],得到各字符的哈夫曼编码 

 2.解码:

  • 构造哈夫曼树依次读入二进制码读入0,则走向左孩子;
  • 读入1,则走向右孩子
  • 一旦到达某叶子时,即可译出字符
  • 然后再从根出发继续译码,指导结束 

 解码示例:

收到如下编码:

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

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

相关文章

PDF文件为什么不能编辑是?是啥原因导致的,有何解决方法

PDF文件格式广泛应用于工作中&#xff0c;但有时候我们可能遇到无法编辑PDF文件的情况。这可能导致工作效率降低&#xff0c;特别是在需要修改文件内容时显得尤为棘手。遇到PDF不能编辑时&#xff0c;可以看看是否以下3个原因导致的。 一、文件受保护 有些PDF文件可能被设置了…

leetcode动态规划(十二)-最后一块石头的重量

题目 1049.最后一块石头的重量 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结…

矩阵matrix

点积 在 NumPy 中&#xff0c;dot 是矩阵或向量的点积&#xff08;dot product&#xff09;操作。 假设有两个向量a和 b&#xff0c;它们的点积定义为对应元素相乘&#xff0c;然后求和。公式如下&#xff1a; 例子&#xff1a; 点积的计算步骤是&#xff1a; 因此&#xf…

入门 | Prometheus+Grafana 普罗米修斯

一、prometheus介绍 1、监控系统组成 一个完整的监控系统需要包括如下功能&#xff1a;数据产生、数据采集、数据存储、数据处理、数据展示、分析、告警等。 &#xff08;1&#xff09;、数据来源 数据来源&#xff0c;也就是需要监控的数据。数据常见的产生、直接或间接暴露…

服务器磁盘爆满?别慌,教你轻松清理!

服务器磁盘爆满&#xff1f;别慌&#xff0c;教你轻松清理&#xff01; 简介 服务器磁盘空间告急&#xff0c;网站访问缓慢&#xff0c;甚至无法正常运行&#xff1f;别担心&#xff0c;这篇文章将为你提供一份详细的清理指南&#xff0c;帮助你快速释放服务器磁盘空间&#x…

【算法】Bellman-Ford单源最短路径算法(附动图)

目录 一、性质 二、思路 三、有边路限制的最短路 一、性质 适用于含有负权边的图&#xff08;Dijkstra不适用&#xff09; 更简单&#xff0c;但效率慢 如果对应路径存在负权回路则没有最短路径&#xff08;可用于判断图中是否存在负权回路&#xff09; 相比于spfa&#…

[分享] SQL在线编辑工具(好用)

在线SQL编写工具&#xff08;无广告&#xff09; - 在线SQL编写工具 - Web SQL - SQL在线编辑格式化 - WGCLOUD

物联网实训项目:绿色家居套件

1、基本介绍 绿色家居通过物联网技术将家中的各种设备连接到一起&#xff0c;提供家电控制、照明控制、电话远程控制、室内外遥控、防盗报警、环境监测、暖通控制、红外转发以及可编程定时控制等多种功能和手段。绿色家居提供全方位的信息交互功能&#xff0c;甚至为各种能源费…

solana phantom NFT图片显示不出来?

solana phantom NFT图片显示不出来&#xff1f; 问题 同样是jpeg格式图片&#xff0c;一个phatom可以显示&#xff0c;一个不可以显示为什么&#xff0c;nft图片格式大小有要求吗&#xff1f; 问题分析 Phantom 官网有一些关于 NFT 集成的文档,其中可能会有关于图片大小限制…

049_python基于Python的热门微博数据可视化分析

目录 系统展示 开发背景 代码实现 项目案例 获取源码 博主介绍&#xff1a;CodeMentor毕业设计领航者、全网关注者30W群落&#xff0c;InfoQ特邀专栏作家、技术博客领航者、InfoQ新星培育计划导师、Web开发领域杰出贡献者&#xff0c;博客领航之星、开发者头条/腾讯云/AW…

15分钟学Go 第7天:控制结构 - 条件语句

第7天&#xff1a;控制结构 - 条件语句 在Go语言中&#xff0c;控制结构是程序逻辑的重要组成部分。通过条件语句&#xff0c;我们可以根据不同的条件采取不同的行动。今天我们将详细探讨Go语言中的两种主要条件结构&#xff1a;if语句和switch语句。理解这些控制结构对于编写…

CTA-GAN:基于生成对抗网络对颈动脉和主动脉的非增强CT影像进行血管增强

写在前面 目前只分析了文章的大体内容和我个人认为的比较重要的细节&#xff0c;代码实现还没仔细看&#xff0c;后续有时间会补充代码细节部分。 文章地址&#xff1a;Generative Adversarial Network-based Noncontrast CT Angiography for Aorta and Carotid Arteries 代…

JAVA基础面试题准备

一些常见的JAVA基础题&#xff0c;面试中遇到过的会加*显示。 JAVA基础 1.Java中重载和重写的区别&#xff1f;* 2.int 和Integer类型这两个区别吗&#xff1f; 为什么需要有Integer类型&#xff1a; int和Integer类型的区别&#xff1a; 3.遍历list有那些方式吗&#xff1f;…

【Linux】进程信号(下)

目录 一、信号的阻塞 1.1 信号在内核中的保存方式 1.2 sigset_t信号集 &#xff08;1&#xff09;信号集操作 &#xff08;2&#xff09;sigprocmask函数 &#xff08;3&#xff09;sigpending函数 二、信号的处理 2.1 用户态和内核态 2.2 重谈进程地址空间 三、信号…

盘点2024年4款高清稳定的Windows10录屏工具。

Windows10电脑录屏在生活当中还是挺重要的&#xff0c;无论是教育领域的制作教程&#xff0c;还是游戏玩家记录精彩瞬间&#xff0c;亦或是商务人士进行演示&#xff0c;录屏都能发挥巨大作用。如果设备自带的一些工具无法完成录屏需求的话&#xff0c;这里帮大家找了几款好用到…

AI大模型应用(3)开源框架Vanna: 利用RAG方法做Text2SQL任务

AI大模型应用(3)开源框架Vanna: 利用RAG方法做Text2SQL任务 RAG&#xff08;Retrieval-Augmented Generation&#xff0c;如下图所示&#xff09;检索增强生成&#xff0c;即大模型LLM在回答问题时&#xff0c;会先从大量的文档中检索出相关信息&#xff0c;然后基于这些检索出…

万家数科:零售业务信息化融合的探索|OceanBase案例

本文作者&#xff1a;马琳&#xff0c;万家数科数据库专家。 万家数科商业数据有限公司&#xff0c;作为华润万家旗下的信息技术企业&#xff0c;专注于零售行业&#xff0c;在为华润万家提供服务的同时&#xff0c;也积极面向市场&#xff0c;为零售商及其生态系统提供全面的核…

挖矿病毒来势汹汹

病毒来了, 我的个人站点使用了 wordpress, 它的不知哪个漏洞让黑客攻入了我的站点 使用 top 命令看到了有不明进程始终占据了 100% 的 CPU snapshot 1 snapshot 2 通过以下 "三板斧"可以查杀这个进程 先用 top (shiftp) 查找占据 CPU 最多的进程根据其进程号 pid 查看…

【数据结构】宜宾大学-计院-实验四

栈和队列之&#xff08;栈的基本操作&#xff09; 实验目的&#xff1a;实验内容&#xff1a;实验结果&#xff1a;实验报告:&#xff08;及时撰写实验报告&#xff09;&#xff1a;实验测试结果&#xff1a;代码实现1.0&#xff1a;&#xff08;C/C&#xff09;【含注释】代码…

QGIS之三十二DEM地形导出三维模型gltf

效果 1、准备数据 (1)dem.tif (2)dom.tif 2、qgis加载dem和dom数据 3、安装插件 插件步骤可以参考这篇文章 QGIS之二十四安装插件 安装了Qgis2threejs插件,结果