哈夫曼编码演示
哈夫曼编码的应用实例
哈夫曼编码的应用实例引言哈夫曼编码是一种常用的数据压缩算法,它通过将出现频率较高的字符用较短的编码表示,从而实现对数据的高效压缩。
本文将通过几个实际应用实例来介绍哈夫曼编码的工作原理和应用场景。
什么是哈夫曼编码哈夫曼编码是由David A. Huffman于1952年提出的一种数据压缩算法。
它通过统计字符的出现频率,然后构建一棵二叉树,将频率较高的字符放在树的较低层,频率较低的字符放在树的较高层,从而实现对数据的压缩。
哈夫曼编码的原理1.统计字符的出现频率:首先需要统计待压缩数据中每个字符的出现频率。
2.构建哈夫曼树:根据字符的出现频率构建一棵哈夫曼树。
构建树的过程中,频率较低的字符被放在树的较高层,频率较高的字符被放在树的较低层。
3.生成哈夫曼编码:从根节点开始,沿着左子树走为0,沿着右子树走为1,将每个字符对应的编码记录下来。
4.进行编码压缩:将待压缩数据中的每个字符用其对应的哈夫曼编码替代。
5.进行解码还原:通过哈夫曼树和编码,将压缩后的数据解码还原为原始数据。
哈夫曼编码的应用实例文本文件压缩文本文件通常包含大量的字符,而且某些字符的出现频率较高。
通过使用哈夫曼编码,可以将出现频率较高的字符用较短的编码表示,从而实现对文本文件的高效压缩。
1.统计字符的出现频率:首先需要对待压缩的文本文件进行字符频率统计,得到每个字符的出现频率。
2.构建哈夫曼树:根据字符的出现频率构建一棵哈夫曼树。
3.生成哈夫曼编码:根据哈夫曼树,为每个字符生成对应的哈夫曼编码。
4.进行编码压缩:将待压缩的文本文件中的每个字符用其对应的哈夫曼编码替代。
5.进行解码还原:通过哈夫曼树和编码,将压缩后的数据解码还原为原始文本文件。
图像压缩图像文件通常包含大量的像素点,每个像素点包含多个颜色信息。
通过使用哈夫曼编码,可以将出现频率较高的颜色用较短的编码表示,从而实现对图像文件的高效压缩。
1.统计颜色的出现频率:首先需要对待压缩的图像文件进行颜色频率统计,得到每个颜色的出现频率。
哈夫曼编码PPT课件
Huffman编码举例
例1【严题集6.26③】:假设用于通信的电文仅由8个字母 {a, b, c, d, e, f, g, h} 构成, 它们在电文中出现的概率分别为{ 0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10 },试 为这8个字母设计哈夫曼编码。如果用0~7的二进制编码方案又如何? 【类同P148 例2】
建议2: Huffman树的存储结构可采用顺序存储结构: 将整个Huffman树的结点存储在一个数组HT[1..n..m]中;
各叶子结点的编码存储在另一“复合”数组HC[1..n]中。
第16页/共60页
Huffman树和Huffman树编码的存储表示:
typedef struct{ unsigned int weight;//权值分量(可放大取整) unsigned int parent,lchild,rchild; //双亲和孩子分量 }HTNode,*HuffmanTree;//用动态数组存储Huffman树 typedef char**HuffmanCode; //动态数组存储Huffman编码表
HT[s1].parent=i; HT[s2].parent=i; HT[i].lchild=s1; HT[i].rchild=s2; HT[i].weight=HT[s1].weight+ HT[s2].weight;}
第18页/共60页
(续前)再求出n个字符的Huffman编码HC
HC=(HuffmanCode)malloc((n+1)*sizeof(char*)); //分配n个字符编码的头指针 向量(一维数组) cd=(char*) malloc(n*sizeof(char)); //分配求编码的工作空间(n)
哈夫曼编码简单例题图
哈夫曼编码简单例题图一、什么是哈夫曼编码1.1 简介哈夫曼编码是一种用于数据压缩的编码方式,由大卫·哈夫曼于1952年发明。
它利用了数据的统计特性,根据出现频率对不同的字符进行编码,将出现频率高的字符用较短的编码表示,出现频率低的字符用较长的编码表示。
1.2 编码原理哈夫曼编码的原理是通过构建哈夫曼树来生成编码表,根据字符出现的频率构建一棵二叉树,出现频率越高的字符离根节点越近,而出现频率越低的字符离根节点越远。
通过遍历哈夫曼树,可生成每个字符对应的编码。
二、哈夫曼编码举例2.1 示例假设有一个包含5个字符的文本文件,字符及其出现频率如下:字符频率A 4B 3C 2D 1E 12.2 构建哈夫曼树1.首先,将字符节点按照出现频率从小到大排序,得到序列:[D, E, C, B,A]。
2.从序列中选取频率最小的两个字符节点(D和E),作为左右子节点构建一个新的节点,该新节点的频率为D和E节点频率之和(1+1=2)。
3.将该新节点插入到序列中,得到新的序列:[C, B, A, DE]。
4.重复第2和第3步,直到序列中只剩下一个节点,即哈夫曼树的根节点。
2.3 生成编码表1.从根节点出发,沿着左子树路径标记0,沿着右子树路径标记1。
2.当到达叶子节点时,记录路径上的编码。
字符频率编码A 4 0B 3 10C 2 110D 1 1110E 1 1111三、哈夫曼编码的应用3.1 数据压缩哈夫曼编码的主要应用是数据压缩。
通过使用哈夫曼编码,出现频率高的字符用较短的编码表示,可以大大减小数据的存储空间。
3.2 信息传输由于哈夫曼编码能够将出现频率高的字符用较短的编码表示,因此在信息传输中使用哈夫曼编码可以提高传输效率,减少传输时间。
3.3 文件加密哈夫曼编码可以用于文件加密。
通过对文件进行编码,可以实现对文件内容的加密和解密,并且只有知道特定的哈夫曼编码表才能正确解密文件。
四、总结哈夫曼编码是一种高效的数据压缩方式,通过构建哈夫曼树和生成编码表,可以将出现频率高的字符用较短的编码表示。
《信息论与编码》第5章哈夫曼编码
什么是哈夫曼编码方法
1952年由美国计算机科学家戴维· 哈夫曼先生提出 是一种数据压缩技术 该方法依据字符出现的概率进行编码 ,其基本思想为: 出现概率高的字符使用较短的编码 出现概率低的则使用较长的编码 使编码之后的码字的平均长度最短
哈夫曼编码方法
哈夫曼编码方法包含两个过程
哈夫曼编码方法包含两个过程
编码过程和译码过程
编码过程 译码过程
构建哈夫曼树 CreatHT(W,&HT)
输入是字符频度表W
表中记录的是原码报文中出现的不同符号个数和频率
输出是哈夫曼树HT
进行哈夫曼译码 HuffmanDecod(HT,CC,W,&OC)
输入的是哈夫曼树HT、代码报文CC和字符频度表W 输出的是原码报文OC
OC
输出OC 到哈夫曼译码系统之外 返回开头
字母a的编码为110 字母n的编码为111
1
4 n
因此,在电文中出现频率 高的字母的编码相对短, 而出现频率低的字母的编 码相对长
111 字符编码表HC=((d,0),(i,10),(a,110),(n,111))
哈夫曼编码过程演示
编码 A1 A2 A3 0.23 0.21 0.18
1
0 1 0 1 0.10 0
编码过程和译码过程
编码过程
构建哈夫曼树 CreatHT(W,&HT)
输入是字符频度表W
表中记录的是原码报文中出现的不同符号个数和频率
输出是哈夫曼树HT
进行哈夫曼编码 HuffmanCoding(HT,&HC)
输入是哈夫曼树HT 输出是字符编码表HC
哈夫曼编码算法
《算法设计与分析》上机报告姓名:学号:日期:上机题目:哈夫曼编码算法实验环境:CPU: ; 内存: 6G ; 操作系统:Win7 64位;软件平台:Visual Studio2008 ;一、算法设计与分析:题目:哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。
其压缩率通常在20%~90%之间。
哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。
一个包含100,000个字符的文件,各字符出现频率不同,如下表所示。
有多种方式表示文件中的信息,若用0,1码表示字符的方法,即每个字符用唯一的一个0,1串表示。
若采用定长编码表示,则需要3位表示一个字符,整个文件编码需要300,000位;若采用变长编码表示,给频率高的字符较短的编码;频率低的字符较长的编码,达到整体编码减少的目的,则整个文件编码需要(45×1+13×3+12×3+16×3+9×4+5×4)×1000=224,000位,由此可见,变长码比定长码方案好,总码长减小约25%。
哈夫曼编码步骤:一、对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F={T1,T2,T3,...,Ti,...,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。
(为方便在计算机上实现算法,一般还要求以Ti的权值Wi的升序排列。
)二、在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
三、从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。
四、重复二和三两步,直到集合F中只有一棵二叉树为止。
二、核心代码:node* huffman(node C[],int n){BuildMaxHeap(C,n);for(int i=1;i<n;i++){node* z = new node;z->left = extract_min(C,n-i+1);z->right = extract_min(C,n - i);z->freq = z->left->freq + z->right->freq;C[n - i] = *z;MaxHeapify(C,n - i,n - i);}return &C[1];}三、结果与分析:其中“?”表示该结点没有字符最优性:二叉树T表示字符集C的一个最优前缀码,x和y是树T中的两个叶子且为兄弟,z 是它们的父亲。
数据结构哈夫曼树和哈夫曼编码PPT课件
C
AB
AC
BC
ABC
第27页/共55页
回朔策略—求幂集
000
000
100
000
010
100
110
000
001
010
011 100 101 110
111
第28页/共55页
回朔策略—求幂集
void powerSet(int num){ if (num<=len-1) { for (int i=0; i<2; i++){ if (i = = 0) mask[num]=1; else mask[num]=0; powerSet(num+1);} } else{ for (int j=0; j<len; j++){ if (mask[j]==1) printf("%c",set[j]);} printf("\n");}
}
第29页/共55页
回朔策略—求幂集
int len=3; int mask[]={0,0,0}; char set[]={'A','B','C'}; int main(int argc, char* argv[]) {
powerSet(0); return 0; }
第30页/共55页
章末复习
1. 熟练掌握二叉树的结构特性,了解相应的证 明方法。 2. 熟悉二叉树的各种存储结构的特点及适用范 围。 3. 遍历二叉树是二叉树各种操作的基础。实现 二叉树遍历的具体算法与所采用的存储结构有 关。掌握各种遍历策略的递归算法,灵活运用 遍历算法实现二叉树的其它操作。层次遍历是 按另一种搜索策略进行的遍历。
哈夫曼编码---PPT
12
结果:
结果:
谢 谢
15
哈夫曼编码实现文件压缩与解压
小组成员:刘 勇 吴风松 张艳芬
1
信源编码的基本途径有两个:
使序列中的各个符号尽可能地互相独立, 即解除相关性;使编码中各个符号源自现的概率尽可能地相 等,即概率均匀化。
2
哈夫曼码为最佳无失真码 哈夫曼编码原理: 哈夫曼编码使用变长编码表对源符号(如文 件中的一个字母)进行编码,可以使编码 之后的字符串的平均长度、期望值降低, 从而达到无损压缩数据的目的。 特点:1.出现机率高的字母使用较短的编 码,反之出现机率低的则使用较长的编码 2.一个短的元素的编码不会成为其他长 3 编码的前缀
5
哈夫曼编码的建模:
为什么要用二叉树的结构来实 现哈夫曼编码?
6
例子说明:
0.4 0.2 0.2 0.1 0.1 0.4 0.2 0.2 0.2 0.4 0.4 0.2
0 1
0.6 0 0.4 1
1.0
0 1
0 1
7
对英文文本文件:
8
霍夫曼树的构造:
9
压缩:
10
解压:
11
程序实现框架:
哈夫曼编码方法
(1)将信源消息符号按其出现的概率大小依次 排列, p1 p2 pn (2)取两个概率最小的字母分别配以0和1两个 码元,并将这两个概率相加作为一个新字 母的概率,与未分配的二进符号的字母重 新排队。
4
(3)对重排后的两个概率最小符号重复步骤 (2) 的过程。
(4) 不断继续上述过程,直到最后两个符号配 以0和1为止。 (5) 从最后一级开始,向前返回得到各个信源 符号所对应的码元序列,即相应的码字。
哈夫曼编码资料讲解
P
0.22 0.20 0.18 0.15 0.10 0.08 0.05 0.02
码字 1 2 3 00 01 02 030 031
例•5-信9 源输出2个符号,概率分布为P=(0.9,0.1),信 源熵H(X)=H(0.9)=0.469。采用二进制哈夫曼编 码。 L=1, 1=1bit/符号; L=2,P’=(0.81,0.09,0.09,0.01), 2=0.645bit/符号; L=3, K 3=0.533bit/符号; L=4, 4=0.493bit/符号。 随着序K列长度L的增加,平均码长迅速降低,接近 信息源熵值,K 编码效率接近于1.
K
一般情况下,信源符号以恒速输出,信道也是恒速传 输的。通过编码后,会造成编码输出每秒的比特数 不是常量,因而不能直接由信道来传送。为了适应 信道,必须增加缓冲寄存器。将编码输出暂存在缓 冲器中,然后再由信道传输,使输入和输出的速率 保持平衡。
溢出:当信源连续输出低概率符号时,因为码长较长, 有可能使缓冲器存不下而溢出。
0110 4
0111 4
该哈夫曼编码的平均码长
7
K p(ai)Ki 2.72码元/符号 i1
信息传输速率
RH(X)2.610.96Bit/码元 K 2.72
哈夫曼编码方法得到的码并非唯一的
1 每次对信源缩减时,赋予信源最后两个概率最小的符号, 用0和1是可以任意的,所以可以得到不同的哈夫曼码,但 不会影响码字的长度。
编码过程
0.4
0.4
0.6 0 1.0
0.2
0.4 0 0.4 1
0.2 0
0.2 1
0.2 1
码字 码长
1
1
01 2
000 3
熵编码详解-huffman和变长编码
变长编码&算术编码
• 利用信源的统计特性进行码率压缩的编码就称为 熵编码,也叫统计编码。
• 视频编码常用的两种:变长编码(也称哈夫曼编
码)及算术编码。现分别讨论其基本原理。
变长编码
1952 年,哈夫曼提出变长编码方法。 其重要的编码原则即: 对出现概率大的符号分配短字长的二进制码, 对出现概率小的符号分配长字长二进制码,得到符号平均 码长最短的码。 变长编码也称最佳编码。 具体编码方法如下所示例子。
• (6)依次类推:
• “a”:L3=0.25+0.125×0=0.25 • H3=0.25+0.125×0.50=0.3125 • R3=0.0625 • “c”:L4=0.25+0.0625×0.75=0.296875 • H4=0.25+0.0625×1.00=0.3125 • R4=0.015625 • “a”:L5=0.296875+0.015625×0=0.296875 • H5=0.296875+0.015625×0.50=0.3046875 • R5=0.0078125
• 哈夫曼曼编码的步骤: • 第一步,将信息符号按其出现概率从大到小排列; • 第二步,将两个最小概率组成一组,划成2 个分支域,并 标以0 和1;再把2 个分支域合并成1个支域,标以两个概 率之和; • 第三步,依次类推,直到概率之和等于1.0; • 第四步,找出概率和1.0 到各信息符号的路径,记下各路 径从右到左各分支域的0 和1,即得到信息符号相应的码字 。(反向编码)
• 设:输入序列为abaca,p(a)=1/2,p(b)= p(c)=1/4,求算术 编码输出序列。
• 解: • 编码: • (1)列出各符号出现概率值
• (2)在(0,1)区间内,每个字符根据其概率选定范围 ,如上表所示 • (3)开始时,浮点范围: • R0=H0-L0=1,H0=1,L0=0。 • (4)当第一个字符“a”被传送时,其范围为[0.00,0.50) ,H=0.05,L=0.00,可得发"a" 字符的范围H 和L: • L1=L0+R0×L=0+1*0 = 0.00 • H1=L0+R0×H=0+1*0.05 = 0.50 • 范围R1=H1-L1=0.50 • 对a 编码后,编码范围从[0, 1)变为[0.00,0.50)。
信息论 第4章(哈夫曼编码和游程编码).ppt
- log2 p(xi)
2.34 2.41 2.48 2.56 2.74 3.34 6.66
码长 3 3 3 3 3 4 7
香农编码分析
可求得该信源的信源熵:
H ( X ) pxi log pxi 2.61(比特/符号) xi X
以及平均码长:
N ni p(xi ) 3.14 (码元/符号) i1
下面是对例1进行哈夫曼编码:
X1:0.20 X2:0.19 X3:0.18 X4:0.17
0.39 0.35
0.61
1.00
X6:0.10 X5:0.15
0.26
X7:0.01 0.11
对应的编码如下:
信源 x1
编码 10
码长 2
x2
x3
x4
x5
x6
x7
11 000 001 010 0110 0111
消息码 标识码 游程长度
该编码方式就称为游程编码(RLC).
例如:有一个信源: BBBBBBBBBBXXXXXXXXJJJJJJJJJAAAAAAAAAAAAA AAAAUUUUUUUUUUUUUUUUUUUU
经过游程编码,得到: B#10X#8J#9A#17U#20
其中#为标识码.
游程编码用于二值图像的压缩
游程编码的基本原理
很多信源产生的消息有一定相关性,往往 连续多次输出同样的消息,同一个消息连续输 出的个数称为游程(Run-Length).我们只需要 输出一个消息的样本和对应重复次数,就完全 可以恢复原来的消息系列.原始消息系列经过 这种方式编码后,就成为一个个编码单元(如下 图),其中标识码是一个能够和消息码区分的特 殊符号.
2.61 2.74
哈夫曼编码
• 哈夫曼(Huffman)编码是一种常用的压缩编码方法, 是Huffman于1952年为压缩文本文件建立的。
• 基本思想 – 通过减少编码冗余来达到压缩的目的。 – 统计符号的出现概率,建立一个概率统计表 • 将最常出现(概率大的)的符号用最短的编码, • 最少出现的符号用最长的编码。
01 20 0 1
40 0 1 0 1 30
0
10
(7)分配码字。将形成的二叉树的左节点标0,右节点 标1。把从最上面的根节点到最下面的叶子节点途中遇到的 0,1序列串起来,就得到了各级灰度的编码.
30 10 20 40 20 40 0 20 20 20 30 30 20 40 40 20
01 20 0 1
霍夫曼编码举例
30 10 20 40 20 40 0 20 20 20 30 30 20 40 40 20
(1) 统计出每级灰度出现的频率: 灰度值: 0 10 20 30 40 出现频率: 1/16 1/16 7/16 3/16 4/16
30 10 20 40 20 40 0 20 20 20 30 30 20 40 40 20
2/16 3/16 5/16 7/16
5/16 30 10 20 40
20 40 0 20
2/16
20 20 30 30
3/16
20 40 40 20
1/16
1/16
(4) 选出频率最小的两个值(2/16,3/16)作为二叉 树的两个叶子节点,将频率和5/16作为它们的根节点,新的 根节点再参与其它频率排序:
(2)从左到右把上述频率按从小到大的顺序排列。 灰度值: 0 10 30 40 20 出现频率: 1/16 1/16 3/16 4/16 7/16
哈夫曼编码详解(C语言实现)
哈夫曼编码详解(C语言实现)哈夫曼编码是一种常见的前缀编码方式,被广泛应用于数据压缩和传输中。
它是由大卫·哈夫曼(David A. Huffman)于1952年提出的,用于通过将不同的字符映射到不同长度的二进制码来实现数据的高效编码和解码。
1.统计字符频率:遍历待编码的文本,记录每个字符出现的频率。
2.构建哈夫曼树:根据字符频率构建哈夫曼树,其中出现频率越高的字符位于树的较低层,频率越低的字符位于树的较高层。
3.生成编码表:从哈夫曼树的根节点开始,遍历哈夫曼树的每个节点,为每个字符生成对应的编码。
在遍历过程中,从根节点到叶子节点的路径上的“0”表示向左,路径上的“1”表示向右。
4.进行编码:根据生成的编码表,将待编码的文本中的每个字符替换为对应的编码。
5.进行解码:根据生成的编码表和编码结果,将编码替换为原始字符。
下面是一个用C语言实现的简单哈夫曼编码示例:```c#include <stdio.h>#include <stdlib.h>#include <string.h>//定义哈夫曼树的节点结构体typedef struct HuffmanNodechar data; // 字符数据int freq; // 字符出现的频率struct HuffmanNode *left; // 左子节点struct HuffmanNode *right; // 右子节点} HuffmanNode;//定义编码表typedef structchar data; // 字符数据char *code; // 字符对应的编码} HuffmanCode;//统计字符频率int *countFrequency(char *text)int *frequency = (int *)calloc(256, sizeof(int)); int len = strlen(text);for (int i = 0; i < len; i++)frequency[(int)text[i]]++;}return frequency;//创建哈夫曼树HuffmanNode *createHuffmanTree(int *frequency)//初始化叶子节点HuffmanNode **leaves = (HuffmanNode **)malloc(256 * sizeof(HuffmanNode *));for (int i = 0; i < 256; i++)if (frequency[i] > 0)HuffmanNode *leaf = (HuffmanNode*)malloc(sizeof(HuffmanNode));leaf->data = (char)i;leaf->freq = frequency[i];leaf->left = NULL;leaf->right = NULL;leaves[i] = leaf;} elseleaves[i] = NULL;}}//构建哈夫曼树while (1)int min1 = -1, min2 = -1;for (int i = 0; i < 256; i++)if (leaves[i] != NULL)if (min1 == -1 , leaves[i]->freq < leaves[min1]->freq) min2 = min1;min1 = i;} else if (min2 == -1 , leaves[i]->freq < leaves[min2]->freq)min2 = i;}}}if (min2 == -1)break;}HuffmanNode *parent = (HuffmanNode*)malloc(sizeof(HuffmanNode));parent->data = 0;parent->freq = leaves[min1]->freq + leaves[min2]->freq;parent->left = leaves[min1];parent->right = leaves[min2];leaves[min1] = parent;leaves[min2] = NULL;}HuffmanNode *root = leaves[min1];free(leaves);return root;//生成编码表void generateHuffmanCode(HuffmanNode *root, HuffmanCode *huffmanCode, char *code, int depth)if (root->left == NULL && root->right == NULL)code[depth] = '\0';huffmanCode[root->data].data = root->data;huffmanCode[root->data].code = strdup(code);return;}if (root->left != NULL)code[depth] = '0';generateHuffmanCode(root->left, huffmanCode, code, depth + 1);}if (root->right != NULL)code[depth] = '1';generateHuffmanCode(root->right, huffmanCode, code, depth + 1);}//进行编码char *encodeText(char *text, HuffmanCode *huffmanCode)int len = strlen(text);int codeLen = 0;char *code = (char *)malloc(len * 8 * sizeof(char));for (int i = 0; i < len; i++)strcat(code + codeLen, huffmanCode[(int)text[i]].code);codeLen += strlen(huffmanCode[(int)text[i]].code);}return code;//进行解码char* decodeText(char* code, HuffmanNode* root) int len = strlen(code);char* text = (char*)malloc(len * sizeof(char)); int textLen = 0;HuffmanNode* node = root;for (int i = 0; i < len; i++)if (code[i] == '0')node = node->left;} elsenode = node->right;}if (node->left == NULL && node->right == NULL) text[textLen] = node->data;textLen++;node = root;}}text[textLen] = '\0';return text;int maichar *text = "Hello, World!";int *frequency = countFrequency(text);HuffmanNode *root = createHuffmanTree(frequency);HuffmanCode *huffmanCode = (HuffmanCode *)malloc(256 * sizeof(HuffmanCode));char code[256];generateHuffmanCode(root, huffmanCode, code, 0);char *encodedText = encodeText(text, huffmanCode);char *decodedText = decodeText(encodedText, root);printf("Original Text: %s\n", text);printf("Encoded Text: %s\n", encodedText);printf("Decoded Text: %s\n", decodedText);//释放内存free(frequency);free(root);for (int i = 0; i < 256; i++)if (huffmanCode[i].code != NULL)free(huffmanCode[i].code);}}free(huffmanCode);free(encodedText);free(decodedText);return 0;```上述的示例代码实现了一个简单的哈夫曼编码和解码过程。
哈夫曼编码译码器代码及运行图示
ch=getchar();
while(ch!='q') /*当键入’q’时程序运行结束*/
{Explain();
while(ch!='r'&&ch!='c'&&ch!='e'&&ch!='d'&&ch!='q')
typedef char * *HuffmanCode;
/*函数声明*/
void readdata(elemtype *w);/*读取字符信息*/
huffmantree createhuff(elemtype *w);/*建立哈弗曼树*/
char * *huffmancode(huffmantree ht);/*哈弗曼编码*/
w[24].data='x' ;w[25].data='y' ;
w[26].data='z' ;
printf("If you input new char weight yourself y/n:\t是否自己输入字符频度y/n:\n");
zf=getchar();
while(zf!='n'&&zf!='y'&&zf!='q')
p->weight=w->weight;
}
else
{p->data='\0';
p->weight=0;
哈夫曼编码
邹健
School of Information and Mathematics
回顾:通信系统模型
信源 信源编码器 信道编码器
干扰 源
调
制
信道
器
编码信道
信宿
信源译码器
解
调
信道译码器
器
➢ 信源:产生消息和消息序列的来源。 ➢ 信源编码器:将信源的输出进行适当的变换,以提高
信息传输的有效性。
School of Information and Mathematics
5
L2 P(si )li 0.4 2 0.2 2 0.2 2 0.1 3 0.1 3 2.2 i 1
码方差
q
2 E[(li L)2 ] P(si )(li L )2
i 1
5
2 1
P(si )(li L1)2 1.36
i 1
5
2 2
P(si )(li L2 )2 0.16
回顾:最优码的构造 ➢ 等长码:每个码字的码长相等 ➢变长编码:每个码字的码长可以不相等
School of Information and Mathematics
引例—色子游戏
顺序编码 是否最优?
School of Information and Mathematics
引例—色子游戏
点数出现 的频率
1 x1 0.4
01 x2 0.2 000 x3 0.2 0010 x4 0.1 0011 x5 0.1
0 0 1 0.2 1
0.4 0 1
0.6 0
1
1.0
School of Information and Mathematics
例题
斐波那契数列的哈夫曼编码
斐波那契数列的哈夫曼编码
斐波那契数列是一个具有第k项是第(k-2)项与第(k-1)项的和的性质的数列。
斐波那契数列的哈夫曼编码可能类似于:
对于斐波那契数列的前n项,我们可以将其别名为a-h,那么他们的编码有如下形式:
a:
b:
c: 000001
d: 00001
e: 0001
f: 001
g: 01
这种编码方式基于斐波那契数列的特性,即每个后续的数字是前两个数字的和。
对于每一个数字,我们都可以将其表示为前两个数字的二进制表示的和。
例如,对于数字5,它等于2+3,所以它的编码为和001(3)的和,即01。
然而,这只是其中一种可能的哈夫曼编码方式,具体的编码方式可能会根据实际的需求和应用而变化。
例如,为了优化编码和解码的时间复杂度,可以使用一些特殊的数据结构和算法。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
//创建并输出编码表 generate_code(root, 0); fout = fopen(“code.txt”, "w"); dump_code(fout); //压缩 fout = fopen(decoded.txt, "w"); encode(fout);
return 0; } 这是我的main函数,为了方便观看做了些 许简省,去掉了关闭文件,清理资源的等 代码行。但仍可以运行。
2
程序介绍
压缩程序
思路: 1.读入文件,统计频率,为出现的每一个字符建立节点,并排成一列 2.将节点转化成哈夫曼树 3.利用哈夫曼树产生编码表文件 4.利用编码将源文件转换成二进制编码表示的文本文件 5.将转码后的文本文件压缩成bin文件
0 1 0 a 0.1 1 b 0.2 c 0.7
产生代码 a: 00 b: 01 c: 1
4
结束感想
本次编写还算成功,虽然调试途中出现了一些大大小小的bug,但是都没有造成很 严重的困扰,可能是因为我采用了老师的框架,绕过了很多陷阱的原因,同时也向 那些自己编写整个程序或是自己不断创新的同学表以敬意。 同时非常高兴的是这是第一次我们能学以致用,编写出能在在生活中得到应用的程 序,这充分体现了一点:科技改善生活。
for(j=0; j<wordnum/8; j++) { character=0; for(i=0; i<8; i++) character += (c = getc(fin)-48) * (int)pow(2,i); fwrite(&character,sizeof(char),1,fout); }
C语言中1个char类型占8bit,在计算机中,也就是8位二进制数,相当 于可以存储0-255大小的十进制数。 所以我们要想把字符串存储转化为二进制存储,只需要将每8位字符串 对应的十进制数计算出来,存入每个char类型。 若最后不足8位,补0至8位即可。
解压缩程序中,只需要将char存储的十进制数通过求余运算即可转化 为二进制数,然后存入字符串即可还原成文本文件,然后再将其解压 缩即可。
2
程序介绍
解压缩程序
思路: 基本思路是找到一个个编码对应的字符还原即可, 步骤如下 1. 将encoded.bin文件转换成二进制编码表示的文 本文件 2. 利用编码表code.txt还原哈夫曼树 3.利用哈夫曼树对文件进行解压缩
主函数: int main() { //初始化 FILE* fout; FILE* fin; root = talloc(); fin = fopen(“code.txt”, "r"); //建树 build_tree(fin); //解码 fin = fopen(encoded.txt, "r"); fout = fopen(“decoded.txt”, "w"); decode(fin, fout); return 0; }
1
总体介绍
可以把哈夫曼编码比作道路优化,原本每个字符占一个char型,也就是8bit,所以 所占空间为 V= 8 * 字符数 (bit) 而编码后,频率高的字符可以用少于8bit表示,而频率低的字符则用多余8bit表示, 这样所占空间为 V= v1*n1+v2*n2+v3*n3+· · · 其中vi代表每个字符所占bit,ni代表字符出现个数,可以证明这样所产生的编码所 占空间是最小的 可以证明这样所得的编码所占空间是最小的。 直观的理解上,就好比城市规划。投入更多的 资金来改善车流量大的道路,而对车流量小的 道路减小投入,这样可使道路资源更充分利用。 在bit的利用上也是如此。
int main(){源自root = pq_pop();
//初始化 struct tnode* p = NULL,* lc, *rc; float freq[255]= {0}; int NCHAR = 255,i = 0; memset(code, 0, sizeof (code)); //统计频率 getfreq(freq); //建立节点队列 for (i = 0; i < NCHAR; i++) if(freq[i]>0) pq_insert(talloc(i, freq[i])); //建立哈夫曼树(删除子节点创建父节点) while(qhead->next!=NULL) { lc = pq_pop(); rc = pq_pop(); p = talloc(0, lc->freq + rc->freq); lc->parent = rc->parent = p; //建立父子关系 p->right = rc; p->left = lc; p->isleaf = 0; pq_insert(p); }
备注:其实不还原哈夫曼树,使用编码表code.txt 读取存储为字符与编码对应的数组或链表,然后 读取encoded.txt,在数组或链表中来找对应编码 还原字符的方法也是可以的,且代码更为简单, 但不如利用哈夫曼树查找还原的效率高。
3
关键问题
按位存储
思路: 按字符串存储的0,1 文件压缩与解压缩是相对容易的,因为可使用 fgetc()等函数读取字符。于是,我们只要在此基础上添加二进制翻译 操作即可。
void generate_code(struct tnode* root, int depth) ——产生编码表 void dump_code(FILE* fp) void encode(FILE* fout) void getfreq(float freq[]) 疑难备注: pq_insert()与pq_pop()两个函数搭配使用,前者用于构建节点队列, 方便遍历节点从而找到最小节点。 后者用于建立哈夫曼树,也就是在建立父节点后在队列中删除那 两个最小的子节点,以免每次找到同样的两个最小节点。 a b c
2
程序介绍
压缩程序
函数: struct tnode* talloc(int symbol, float freq) void pq_insert(struct tnode* p) struct tnode* pq_pop() ——为字符创建节点 ——将节点插入升序排列的队列 ——将队列中第一个(最小频率)节点删除 ——输出编码表 ——压缩文件 ——统计字符频率
哈夫曼编码与文件压缩
——1236003 6120610319 杜嘉诚
内容
1
总体介绍
2
程序介绍
3
关键问题
4
结束感想
1
总体介绍
哈夫曼编码是一种编码方式,经常用于数据压缩。在计算机信息处理中,哈夫 曼编码是一种一致性编码法(又称“熵编码法”),用于数据的无损耗压缩。
它的基本原理是建立一张编码表将原字符(例如某文件的一个符号)进行重新编 码。它是根据每一个源字符出现的概率而建立起来的,这些字符(如字母)出现的 次数越多,其编码的位数就越少。这时的编码之后的字符串的平均期望长度降 低,从而达到无损压缩数据的目的)。 这种方法是由David.A.Huffman发展起来的,故称为哈夫曼编码。
3
关键问题
按位存储
由字符串翻译为二进制: void translatetobin(FILE *fout,FILE *fin,int wordnum) { int j,i,character; unsigned char c;
· · //翻译:一共有总数/8组数据,每组利用a*20+b*21+c*22· 计算后输出即可
谢谢观看
}
3
关键问题
按位存储
由二进制翻译为字符串: Void translatetochar(FILE *fin,FILE *fout) { unsigned char c,d; int i; //翻译:一共有总数/8组数据,每组利用短除法计算后输出即可 while (fread(&c,sizeof(char),1,fin)) { for(i=0;i<8;i++) { fprintf(fout,"%c",d=c%2+48); c=c/2; } } }