6.10哈夫曼编码与构造

2023-12-29 20:30
文章标签 构造 编码 哈夫曼 6.10

本文主要是介绍6.10哈夫曼编码与构造,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

6.10哈夫曼编码与构造

哈夫曼编码
由于哈夫曼树是具有相同叶子个数的二叉树中带权路径长度最小的二叉树,涉及求根据给定叶子(带权)求其“规模最小”的二叉树问题,用哈夫曼树构造哈夫曼编码就是其典型应用。

1.哈夫曼编码的概念

用电子方式处理符号时,需先对符号进行二进制编码。例如,在计算机中使用的英文字 符的 ASCII 编码就是 8 位二进制编码,ASCII 编码是一种定长编码,即每个字符用相同数目的二进制位编码。

为了缩短数据文件(报文)长度,可采用不定长编码。其基本思想是,给使用频度较高 的字符编以较短的编码。这是数据压缩技术的最基本的思想。如何给数据文件中的字符编以不定长编码,使各种数据文件平均最短呢?这也是个与哈夫曼树相关的最优问题。通过实例介绍概念。

  • (1)前缀码:如果在一个编码系统中,任一个编码都不是其他任何编码的前缀(最左 子串),则称该编码系统中的编码是前缀码。例如,一组编码 01,001,010,100,110 就不 是前缀码,因为 01 是 010 的前缀,若去掉 01 或 010 就是前缀码。例如,名字中的郑霞、郑霞锦就不是前缀码。
  • 若是前缀码,则在电文中,各字符对应的编码之间不需要分隔符。如果不是前缀码,则 若不使用分隔符,会产生二义性。
  • (2)哈夫曼编码:对一棵具有 n 个叶子的哈夫曼树,若对树中的每个左分支赋予 0, 右分支赋予 1(也可规定左 1 右 0),则从根到每个叶子的通路上,各分支的赋值分别构成一 个二进制串,该二进制串就称为哈夫曼编码。

下面给出哈夫曼编码的相关特性。

定理 6-1 哈夫曼编码是前缀码。

  • 证明:哈夫曼编码是根到叶子路径上的边的编码的序列,也就是等价边序列,而由树的 特点可知,若路径 A 是另一条路经 B 的最左部分,则 B 经过了 A,因此,A 的终点不是叶子。 而哈夫曼编码都对应终点为叶子的路径,所以,任一哈夫曼码都不会与任意其他哈夫曼编码 的前部分完全重叠,因此哈夫曼编码是前缀码。

定理 6-2 哈夫曼编码是最优前缀码。
即对于 n 个字符,分别以它们的使用频度为叶子权构造哈夫曼树,则该树对应的哈夫曼编码能使各种报文(由这 n 种字符构成的文本)对应的二进制串的平均长度最短。
在这里插入图片描述
由哈夫曼树的构造方法知,使用频度较高的字符对应的编码较短,这也直观地说明了本定理。

2.哈夫曼编码的作用

哈夫曼树被广泛的应用,最典型的就是在编码技术上的应用。利用哈夫曼树,可以得到平均长度最短的编码。

研究操作码的优化问题主要是为了缩短指令字的长度,减少程序的总长度以及增加指令所能表示的操作信息和地址信息。要对操作码进行优化,就要知道每种操作指令在程序 中的使用频率。这一般是通过对大量已有的典型程序进行统计得到的。

这里以计算机操作码的优化问题为例来分析说明。

例 6-7 设有一台模型机,共有 7 种不同的指令,其使用频率如表 6-5 所示

在这里插入图片描述
由于计算机内部只能识别 0、1 代码,所以若采用定长操作码,则需要 3 位(23=8)。 一段程序中若有 n 条指令,每条指令编码占 3 位,那么程序的总位数为 3×n。为了充分地 利用编码信息和减少程序的总位数,我们可以采用变长编码。如果对每一条指令指定一条编码,并使这些编码互不相同且最短,是否可以满足要求呢?可否采用如表 6-6 所示这样编码呢?

在这里插入图片描述

这样虽然可以使得程序的总位数达到最小,但机器却无法解码。例如,对编码串 0010110 该怎么识别呢?第一个 0 可以识别为 I1,也可以和第二个 0 组成的串 00 一起被识别为 I3, 还可以将前三位识别为 I6,这样一来,这个编码串就有多种译法。因此,若要设计变长的编 码,则这种编码必须满足这样一个条件:任意一个编码不能成为其他任意编码的前缀。把满足这个条件的编码叫做前缀编码

利用哈夫曼算法,就可以设计出最优的前缀编码。首先,以每条指令的使用频率为权值构造哈夫曼树,据表 6-5 构造的哈夫曼树如图所示。
在这里插入图片描述

对于该哈夫曼树,规定向左的分支标记为 0,向右的分支标记为 1,如下图所示。这样, 从根结点开始,沿线到达各频度指令对应的叶结点,每个叶结点对应的编码长度不等,但最 长不超过 n,所经过的分支代码序列,就构成了相应频度指令的哈夫曼编码,如表 6-7 所示。
在这里插入图片描述
在这里插入图片描述

3.哈夫曼编码的算法实现

typedef  char *  HuffmanCode[N+1];   /* 存储哈夫曼编码串的头指针数组 */ 

由于每个哈夫曼编码是变长编码,因此使用指针数组存放每个编码串的头指针。

算法描述】 求哈夫曼树的哈夫曼编码的算法

void CrtHuffmanCode(HuffmanTree  ht,  HuffmanCode  hc,  int n)
/*从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码*/ 
{ char *cd; cd=(char * )malloc(n* sizeof(char ));         /*分配求当前编码的工作空间*/ cd[n-1]=’\0’;      /*从右向左逐位存放编码,首先存放编码结束符*/ for(i=1;i<=n;i++)     /*求 n 个叶子结点对应的哈夫曼编码*/ { start=n-1;             /*初始化编码起始指针*/ c=i; p=ht[i].parent;    /* 从叶子结点开始向上倒推 */ while(p!=0) { --start; if(ht[p].LChild==c)  cd[start]=0;       /*左分支标 0*/ else  cd[start]=1;                    /*右分支标 1*/ c=p;  p=ht[p].parent;                  /* 向上倒推 */ }   hc[i]=(char *)malloc((n-start)*sizeof(char));    /*为第 i 个编码分配空间*/ strcpy(hc[i], &cd[start]);                    /*把编码复制到 hc[i]中*/ } free(cd); 
} 

这篇关于6.10哈夫曼编码与构造的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/550624

相关文章

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

Python使用自带的base64库进行base64编码和解码

《Python使用自带的base64库进行base64编码和解码》在Python中,处理数据的编码和解码是数据传输和存储中非常普遍的需求,其中,Base64是一种常用的编码方案,本文我将详细介绍如何使... 目录引言使用python的base64库进行编码和解码编码函数解码函数Base64编码的应用场景注意

VSCode中C/C++编码乱码问题的两种解决方法

《VSCode中C/C++编码乱码问题的两种解决方法》在中国地区,Windows系统中的cmd和PowerShell默认编码是GBK,但VSCode默认使用UTF-8编码,这种编码不一致会导致在VSC... 目录问题方法一:通过 Code Runner 插件调整编码配置步骤方法二:在 PowerShell

Python如何实现读取csv文件时忽略文件的编码格式

《Python如何实现读取csv文件时忽略文件的编码格式》我们再日常读取csv文件的时候经常会发现csv文件的格式有多种,所以这篇文章为大家介绍了Python如何实现读取csv文件时忽略文件的编码格式... 目录1、背景介绍2、库的安装3、核心代码4、完整代码1、背景介绍我们再日常读取csv文件的时候经常

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr

form表单提交编码的问题

浏览器在form提交后,会生成一个HTTP的头部信息"content-type",标准规定其形式为Content-type: application/x-www-form-urlencoded; charset=UTF-8        那么我们如果需要修改编码,不使用默认的,那么可以如下这样操作修改编码,来满足需求: hmtl代码:   <meta http-equiv="Conte

4-4.Andorid Camera 之简化编码模板(获取摄像头 ID、选择最优预览尺寸)

一、Camera 简化思路 在 Camera 的开发中,其实我们通常只关注打开相机、图像预览和关闭相机,其他的步骤我们不应该花费太多的精力 为此,应该提供一个工具类,它有处理相机的一些基本工具方法,包括获取摄像头 ID、选择最优预览尺寸以及打印相机参数信息 二、Camera 工具类 CameraIdResult.java public class CameraIdResult {

C++中类的构造函数调用顺序

当建立一个对象时,首先调用基类的构造函数,然后调用下一个派生类的 构造函数,依次类推,直至到达派生类次数最多的派生次数最多的类的构造函数为止。 简而言之,对象是由“底层向上”开始构造的。因为,构造函数一开始构造时,总是 要调用它的基类的构造函数,然后才开始执行其构造函数体,调用直接基类构造函数时, 如果无专门说明,就调用直接基类的默认构造函数。在对象析构时,其顺序正好相反。