哈弗曼编码课程设计实验报告

合集下载

哈夫曼编码问题总结

哈夫曼编码问题总结

哈夫曼编码问题总结
哈夫曼编码是一种常见的数据压缩算法,但在实践中常常遇到各种问题。

以下是哈夫曼编码问题的总结:
1. 频率表不准确:哈夫曼编码的核心是根据字符出现的频率构建树结构,如果频率表不准确,树的构建就会出现问题。

因此,在使用哈夫曼编码前,需要确保频率表的准确性。

2. 编码长度不一:哈夫曼编码是一种变长编码,不同字符的编码长度不一定相同。

如果出现某个字符的编码长度过长,就会导致压缩效果变差。

为了避免这种情况,可以使用贪心算法来构建哈夫曼树,使得每个字符的编码长度尽可能短。

3. 解码效率低:在解码哈夫曼编码时,需要遍历整个哈夫曼树,查找对应的字符。

如果哈夫曼树过大,解码效率就会变低。

为了提高解码效率,可以使用哈夫曼编码的反向索引表来加速查找。

4. 压缩效果不佳:尽管哈夫曼编码可以大幅度减少数据的存储空间,但在某些情况下,压缩效果可能不如其他算法。

例如,对于随机分布的数据,哈夫曼编码的效果很差。

因此,在选择数据压缩算法时,需要根据具体情况进行选择。

总之,哈夫曼编码是一种强大的数据压缩算法,但在实践中需要注意以上问题。

只有对这些问题有充分的了解和掌握,才能更好地运用哈夫曼编码来实现数据压缩。

- 1 -。

哈夫曼编码python

哈夫曼编码python

哈夫曼编码python一、什么是哈夫曼编码?哈夫曼编码(Huffman Coding)是一种可变长度编码(Variable Length Code),它可以将不同长度的字符编码成等长的二进制串,从而实现数据压缩的目的。

哈夫曼编码是由David A. Huffman在1952年发明的,它是一种贪心算法,可以得到最优解。

二、哈夫曼编码原理1.字符频率统计在进行哈夫曼编码之前,需要先统计每个字符出现的频率。

通常使用一个字典来存储每个字符和其出现的次数。

2.构建哈夫曼树根据字符出现频率构建一个二叉树,其中频率越高的字符离根节点越近。

构建过程中需要用到一个优先队列(Priority Queue),将每个节点按照频率大小加入队列中,并将队列中前两个节点合并为一个新节点,并重新加入队列中。

重复这个过程直到只剩下一个节点,即根节点。

3.生成哈夫曼编码从根节点开始遍历哈夫曼树,在遍历过程中,左子树走0,右子树走1,直到叶子节点。

将路径上经过的0和1分别表示为0和1位二进制数,并把这些二进制数拼接起来,就得到了该字符的哈夫曼编码。

三、哈夫曼编码Python实现下面是一个简单的Python实现:1.字符频率统计```pythonfrom collections import Counterdef get_char_frequency(text):"""统计每个字符出现的频率"""return Counter(text)```2.构建哈夫曼树```pythonimport heapqclass HuffmanNode:def __init__(self, char=None, freq=0, left=None, right=None): self.char = charself.freq = freqself.left = leftself.right = rightdef __lt__(self, other):return self.freq < other.freqdef build_huffman_tree(char_freq):"""根据字符频率构建哈夫曼树"""nodes = [HuffmanNode(char=c, freq=f) for c, f inchar_freq.items()]heapq.heapify(nodes)while len(nodes) > 1:node1 = heapq.heappop(nodes)node2 = heapq.heappop(nodes)new_node = HuffmanNode(freq=node1.freq+node2.freq, left=node1, right=node2)heapq.heappush(nodes, new_node)return nodes[0]```3.生成哈夫曼编码```pythondef generate_huffman_codes(node, code="", codes={}): """生成哈夫曼编码"""if node is None:returnif node.char is not None:codes[node.char] = codegenerate_huffman_codes(node.left, code+"0", codes) generate_huffman_codes(node.right, code+"1", codes)return codes```四、使用哈夫曼编码进行压缩使用哈夫曼编码进行压缩的方法很简单,只需要将原始数据中的每个字符用对应的哈夫曼编码替换即可。

哈夫曼编码方法的选择及其Python的实现

哈夫曼编码方法的选择及其Python的实现

• 188•随着人们对图像和视频的压缩存储和传输的要求越来越高,如何提高传输速率和如何节省存储空间显得更加重要,解决这两个问题的最根本途径就是采用图像压缩技术。

本科课程《信息论与编码》中指出图像压缩的具体实现技术就是压缩编码,通过编码可以减少信息的冗余度从而提高传输速率和节省存储空间。

在音视频编解码技术快速发展的今天,其实已经有很多的编码方法。

通过实际应用发现,哈夫曼编码在编码效率与平均码长方面都是较好的。

本文主要研究哈夫曼编码及其Python 的实现。

1 哈夫曼编码哈夫曼编码是一种典型的无失真编码,哈夫曼编码所采用的编码原理是最佳编码定理。

最佳编码定理指出,在信息编码的过程中对于信源符号,如果分配短字长的码字给出现概率小的信源符号,分配长字长的码字给出现概率大的信源符号,那么编码结束之后所得到的平均码长一定是小于其他任何一种编码方法所得到的平均码长的,也就是每个信源符号所得到的码字长度是严格按照符号概率大小的相反顺序所排列。

哈夫曼编码具体步骤如下:(1)将n 个信源符号按其概率大小进行降序排序,即:p (x 1) ≥ p (x 2) ≥ p (x 3)≥……≥ p (x n ) (1)(2)取两个概率最小的信源符号分别配以1和0两个码元,然后将这两个信源符号概率相加作为一个新符号的概率,与未分配的二进符号重新进行降序排序。

(3)对重排后的序列重复(2)过程,直到只有两个信源符号为止,再把这两个信源符号分别配以1和0即可。

(4)最后得出各个符号的码字。

2 哈夫曼编码方法的选择及Python的实现哈夫曼编码方法的选择和Python 的实现看似是两个分离的部分,但其实两者是有机结合的,因为要对哈夫曼编码方法做出选择就要通过Python 的实现来分析各种方法的平均码长和编码效率。

由哈夫曼编码的具体步骤可以看出来:哈夫曼的编码结果其实是不唯一的。

这是因为:其一,对两个概率最小的信源符号0和1的分配是任意的;其二,当两个概率最小的信源符号的概率相加时,所得的概率值有可能与原序列中的其他概率值相等。

哈夫曼编码方式对字母进行不定长的二进制编码

哈夫曼编码方式对字母进行不定长的二进制编码

哈夫曼编码方式对字母进行不定长的二进制编码哈夫曼编码是一种常用于数据传输和数据压缩的编码方式。

它可以对不同的符号(比如字母或者其他字符)进行不定长的二进制编码,从而实现高效的数据压缩。

在这篇文章中,我将会对哈夫曼编码进行深入的探讨,从原理到应用,让你更全面地了解这一编码方式的重要性和价值。

在介绍哈夫曼编码前,我们先来了解一下传统的固定长度编码和变长编码。

固定长度编码是将每个符号都用固定长度的二进制序列表示,比如ASCII码就是一种固定长度编码方式。

这种编码方式的缺点是不适合对出现频率不同的符号进行编码,会浪费大量的存储空间。

而变长编码则可以根据符号的出现频率来灵活地进行编码,从而实现更高效的数据压缩。

哈夫曼编码就是一种典型的变长编码方式,它是由大卫·哈夫曼在1952年提出的。

在哈夫曼编码中,根据符号出现的频率不同,使用不同长度的二进制序列表示。

出现频率高的符号对应较短的二进制序列,而出现频率低的符号对应较长的二进制序列。

这样一来,可以实现对数据进行高效压缩,减少存储空间的占用。

哈夫曼编码的原理非常巧妙,通过构建哈夫曼树来实现对符号的编码。

在构建哈夫曼树的过程中,首先将所有的符号按照出现频率进行排序,然后将出现频率最低的两个符号合并为一个新的符号,其出现频率为两者之和。

不断重复这个过程,直到所有的符号都被合并在一棵树中。

哈夫曼树的叶子节点就对应着每个符号及其对应的二进制编码。

通过哈夫曼编码,我们可以实现对数据的高效压缩。

这种编码方式在通信和存储领域有着广泛的应用,比如在JPEG、MP3等格式中都采用了哈夫曼编码来对数据进行压缩。

这不仅可以节省存储空间,还可以提高数据传输的效率,是一种非常重要和实用的编码方式。

从个人的角度来看,我认为哈夫曼编码是一种非常巧妙和高效的编码方式。

它充分利用了符号出现频率的特点,实现了对数据的高效压缩,为数据传输和存储提供了重要的技术支持。

哈夫曼编码的应用领域非常广泛,可以说几乎涵盖了所有需要数据压缩的领域。

哈夫曼编码1952年Huffma...

哈夫曼编码1952年Huffma...

第三章多媒体数据压缩3.1 数据压缩的基本原理和方法3.1 数据压缩的基本原理和方法•压缩的必要性音频、视频的数据量很大,如果不进行处理,计算机系统几乎无法对它进行存取和交换。

例如,一幅具有中等分辨率(640×480)的真彩色图像(24b/像素),它的数据量约为7.37Mb/帧,一个100MB(Byte)的硬盘只能存放约100帧图像。

若要达到每秒25帧的全动态显示要求,每秒所需的数据量为184Mb,而且要求系统的数据传输率必须达到184Mb/s。

对于声音也是如此,若采用16b样值的PCM编码,采样速率选为44.1kHZ ,则双声道立体声声音每秒将有176KB的数据量。

3.1 数据压缩的基本原理和方法•视频、图像、声音有很大的压缩潜力信息论认为:若信源编码的熵大于信源的实际熵,该信源中一定存在冗余度。

原始信源的数据存在着很多冗余度:空间冗余、时间冗余、视觉冗余、听觉冗余等。

3.1.1 数据冗余的类型•空间冗余:在同一幅图像中,规则物体和规则背景的表面物理特性具有相关性,这些相关性的光成像结果在数字化图像中就表现为数据冗余。

–一幅图象中同一种颜色不止一个象素点,若相邻的象素点的值相同,象素点间(水平、垂直)有冗余。

–当图象的一部分包含占主要地位的垂直的源对象时,相邻线间存在冗余。

3.1.1 数据冗余的类型•时间冗余:时间冗余反映在图像序列中就是相邻帧图像之间有较大的相关性,一帧图像中的某物体或场景可以由其它帧图像中的物体或场景重构出来。

–音频的前后样值之间也同样有时间冗余。

–若图象稳定或只有轻微的改变,运动序列帧间存在冗余。

3.1.1 数据冗余的类型•信息熵冗余:信源编码时,当分配给第i 个码元类的比特数b (y i )=-log p i ,才能使编码后单位数据量等于其信源熵,即达到其压缩极限。

但实际中各码元类的先验概率很难预知,比特分配不能达到最佳。

实际单位数据量d>H (S ),即存在信息冗余熵。

哈夫曼编码详解

哈夫曼编码详解

哈夫曼编码详解前两天发布那个 rsync 算法后,想看看数据压缩的算法,知道⼀个经典的压缩算法 Huffman 算法。

你应该听说过和他的经典的压缩算法—— ,这是⼀种通过字符出现频率,,和⼆叉树来进⾏的⼀种压缩算法,这种⼆叉树⼜叫 Huffman ⼆叉树 —— ⼀种带权重的树。

但是⽹上查了⼀下,中⽂社区内好像没有把这个算法说得很清楚的⽂章,尤其是树的构造,⽽正好看到⼀篇国外的⽂章《》,其中的例⼦浅显易懂,相当不错,我就转了过来。

注意,我没有对此⽂完全翻译。

我们直接来看⽰例,如果我们需要来压缩下⾯的字符串: “beep boop beer!” ⾸先,我们先计算出每个字符出现的次数,我们得到下⾯这样⼀张表 :字符次数‘b’3‘e’4‘p’2‘ ‘2‘o’2‘r’1‘!’1 然后,我把把这些东西放到 Priority Queue 中(⽤出现的次数据当 priority),我们可以看到,Priority Queue 是以 Prioirry 排序⼀个数组,如果 Priority ⼀样,会使⽤出现的次序排序:下⾯是我们得到的 Priority Queue: 接下来就是我们的算法——把这个 Priority Queue 转成⼆叉树。

我们始终从 queue 的头取两个元素来构造⼀个⼆叉树(第⼀个元素是左结点,第⼆个是右结点),并把这两个元素的 priority 相加,并放回 Priority 中(再次注意,这⾥的 Priority 就是字符出现的次数),然后,我们得到下⾯的数据图表: 同样,我们再把前两个取出来,形成⼀个 Priority 为2+2=4的结点,然后再放回 Priority Queue 中 : 继续我们的算法(我们可以看到,这是⼀种⾃底向上的建树的过程): 最终我们会得到下⾯这样⼀棵⼆叉树: 此时,我们把这个树的左⽀编码为0,右⽀编码为1,这样我们就可以遍历这棵树得到字符的编码,⽐如:‘b’的编码是 00,’p'的编码是101, ‘r’的编码是 1000。

哈夫曼编码算法

《算法设计与分析》上机报告姓名:学号:日期:上机题目:哈夫曼编码算法实验环境: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 是它们的父亲。

哈夫曼编码

设计哈夫曼编码【问题描述】根据给定字符的使用频率,为其设计哈夫曼编码。

【基本要求】●功能:求出n个字符的哈夫曼编码。

●输入:输入n个字符和字符在电文中的使用频率。

●输出:n个字符的哈夫曼编码。

【测试数据】输入字符集{a,b,c,d,e,f,g,h}的使用频率{5,29,7,8,14,23,3,11}(扩大100倍),预期的输出为这8个字符的哈夫曼编码a:0001;b:10;c:1110;d:1111;e:110;f:01;h:001 。

【数据结构】哈夫曼树中没有度数为1的分支结点,有n个叶子结点的哈夫曼树中共有2n-1个结点,可以用大小为2n-1的数组来存储哈夫曼树中的结点。

在哈夫曼算法中,对每个结点,既需要知道双亲的信息,又需要知道其孩子结点的信息,因此,其存储结构为:#define n 8 /*叶子数目*/#define m 2*n-1 /*结点总数*/typedef struct{int weight; /*结点的权值*/int lchild, rchild, parent; /*左、右孩子及双亲的下标*/}htnode;typedef htnode humffmantree[m+1]; /* humffmantree结构数组类型,其0号单元不用*/ humffmantree ht;其中,每个结点包括4个域,weight域存放结点的权值;lchild、rchild域分别为结点的左右孩子在数组中的下标,叶子结点的这两个域的值为0;parent域是结点的双亲在数组中的下标。

这里设置parent域不仅仅是为了使涉及双亲的运算方便,它的主要作用是根和非根结点。

之所以要区分根与非跟结点,是因为在当前森林中合并两棵二叉树时,必须在森林的所有结点中先取两个权值最小的根结点,因此,有必要为每个结点设置一个标记以区分根和非跟结点为实现方便,将n个叶子结点集中存储在前面的n个位置(下标为1-n),后面n-1个位置存储n-1个非叶子结点。

信息论与编码实验报告

信息论与编码实验报告一、实验目的信息论与编码是一门涉及信息的度量、传输和处理的学科,通过实验,旨在深入理解信息论的基本概念和编码原理,掌握常见的编码方法及其性能评估,提高对信息处理和通信系统的分析与设计能力。

二、实验原理(一)信息论基础信息熵是信息论中用于度量信息量的重要概念。

对于一个离散随机变量 X,其概率分布为 P(X) ={p(x1), p(x2),, p(xn)},则信息熵H(X) 的定义为:H(X) =∑p(xi)log2(p(xi))。

(二)编码原理1、无失真信源编码:通过去除信源中的冗余信息,实现用尽可能少的比特数来表示信源符号,常见的方法有香农编码、哈夫曼编码等。

2、有噪信道编码:为了提高信息在有噪声信道中传输的可靠性,通过添加冗余信息进行纠错编码,如线性分组码、卷积码等。

三、实验内容及步骤(一)信息熵的计算1、生成一个离散信源,例如信源符号集为{A, B, C, D},对应的概率分布为{02, 03, 01, 04}。

2、根据信息熵的定义,使用编程语言计算该信源的信息熵。

(二)香农编码1、按照香农编码的步骤,首先计算信源符号的概率,并根据概率计算每个符号的编码长度。

2、确定编码值,生成香农编码表。

(三)哈夫曼编码1、构建哈夫曼树,根据信源符号的概率确定树的结构。

2、为每个信源符号分配编码,生成哈夫曼编码表。

(四)线性分组码1、选择一种线性分组码,如(7, 4)汉明码。

2、生成编码矩阵,对输入信息进行编码。

3、在接收端进行纠错译码。

四、实验结果与分析(一)信息熵计算结果对于上述生成的离散信源,计算得到的信息熵约为 184 比特/符号。

这表明该信源存在一定的不确定性,需要一定的信息量来准确描述。

(二)香农编码结果香农编码表如下:|信源符号|概率|编码长度|编码值|||||||A|02|232|00||B|03|174|10||C|01|332|110||D|04|132|111|香农编码的平均码长较长,编码效率相对较低。

哈夫曼编码与译码(附源码).

建立Huffman树进行编码和译码的设计李香兰 2015548947 哈尔滨工业大学计算机科学与技术学院 1501班摘要:建立一个简易的系统,对于给定的一篇英文文章,统计字符出现的概率,并根据概率建立Huffman树,利用Huffman编码对文章进行编码和译码。

掌握Huffman树的建立与应用,并进一步熟练掌握程序的设计流程。

关键词:Huffman树Huffman编码文章译码文件压缩解压缩1.引言:给定一篇文章,统计字符出现的概率,根据概率建立哈夫曼树,并进行哈夫曼编码,进而可以利用哈夫曼编码对文章进行编码与译码和文件压缩、解压缩等操作。

2.程序设计流程(1)文字表述开始进入功能选择界面,包含五种操作:1.读取文章并对字符编码,2.哈夫曼编码信息,3.文章编码,4.文章译码,5.文件压缩,6.文件解压缩,7.退出程序。

操作1:给定一篇文章,统计字符出现的概率,并根据概率建立Huffman树,并利用Huffman树对字符进行Huffman编码。

操作2:显示Huffman编码信息,包括字符,字符出现的概率,Huffman编码。

操作3:对文章进行译码,显示译码信息,并保存。

操作4:对文章进行译码,显示并保存。

操作5:对文件进行压缩,每7位二进制序列对应一个ASCII码。

操作6:对文件进行解压缩。

(2)流程图程序开始程序主界面读取文章并对字符编码哈夫曼编码信息文章编码文章译码退出程序显示文章编码保存文章编码返回上一界面显示文章编码的译码保存文章编码的译码程序结束文件压缩文件解压缩(3)程序数据要求及功能实现主界面1.读取文件并对字符进行编码2.哈夫曼编码信息3.文件编码(1)显示文件编码(2)保存文件编码4.文件译码(1)显示文章编码的译码(2)保存文章编码的译码5.文件压缩6.文件解压缩附:程序源代码/** File: HUFFMANFUNCTION.h* Author: Administrator** Created on 2011年12月19日, 下午6:19 */#ifndef HUFFMANFUNCTION_H#define HUFFMANFUNCTION_H#include <cstdlib>#include<iostream>#include<fstream>#include<math.h>#define max1 150#define max2 50#define max3 256using namespace std;class Htnote {public:char name; //字符名double weight; //权重int lchild; //左孩子int rchild; //右孩子int parent; //父亲Htnote() {weight = 0;lchild = -1;parent = -1;rchild = -1;}};class Name {public:int num; //字符出现的次数char pname; //字符名double lweight; //权值Name() {num = 0;lweight = 0;}};class GetName {public:char namef[max2];int n; //字符的种类int sum; //字符的总数Name letter[max1]; //存储字符信息的类的数组GetName() {sum = 0;n = 0;}void GetWeight()//得到字符的权值{for (int i = 0; i < n; i++) {letter[i].lweight = (double) letter[i].num / sum; //出现的次数除总数得到权值}}int ReadLetter() {ifstream input;cout << "请输入文件名:" << endl;cin >> namef;input.open(namef); //打开文件if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}char ch;ch = input.get();letter[0].pname = ch;letter[0].num++;sum++;while (!input.eof()) {//读取文件中的所有字符int tag = 0;ch = input.get();for (int i = 0; i < n + 1; i++) {if (letter[i].pname == ch) {letter[i].num++;sum++;tag = 1;}}if (tag == 0) {n++;letter[n].pname = ch;letter[n].num++;sum++;}}sum--;input.close();GetWeight(); //得到字符权值}};class CodeNode//编码类{public:char ch; //存储字符char bits[max1]; //存储编码};class Function {public:GetName L;int fn; //定义哈夫曼数组大小Htnote HuffmanT[max3]; //哈夫曼数组CodeNode Code[max1]; //字符编码数组Function() {fn = 0;}void CharHuffmanTCoding()//编码功能实现{int i, f, c;char cd[L.n + 1]; //暂时存储编码的数组int start; //编码读取起始位置cd[L.n] = '\0';for (i = 0; i < L.n; i++) {Code[i].ch = HuffmanT[i].name; //字符信息start = L.n; //起始位置c = i;while ((f = HuffmanT[c].parent) >= 0) {if (HuffmanT[f].lchild == c)//如果为左孩子,为‘0’{cd[--start] = '0';} else//如果为右孩子,为‘1’{cd[--start] = '1';}c = f;}strcpy(Code[i].bits, &cd[start]); //将结果存入对应的编码数组中}}void OutputHuffmanTCode() {cout << "哈夫曼编码:" << endl;cout << "——————————————————————" << endl;cout << "字符\t权值\t\t哈夫曼编码" << endl;for (int i = 0; i < L.n; i++)//输出字符,权值,哈夫曼编码{cout << "——————————————————————" << endl;cout << HuffmanT[i].name << "\t" << HuffmanT[i].weight << "\t";cout << Code[i].bits;cout << endl;}cout << "——————————————————————" << endl;}void InitHT()//哈夫曼初始化{L.ReadLetter();fn = (L.n)*2 - 1;for (int i = 0; i < fn; i++) {if (i < L.n) {HuffmanT[i].name = L.letter[i].pname;HuffmanT[i].weight = L.letter[i].lweight;}}}void SelectMin(int m, int &p1, int &p2)//选择最小的两个节点{int i, j;double m1, m2;m1 = m2 = 1;p1 = p2 = -1;for (i = 0; i < m; i++) {if (HuffmanT[i].parent == -1 && HuffmanT[i].weight < m1)//找出为访问过的最小权值节点{m2 = m1;p2 = p1;m1 = HuffmanT[i].weight;p1 = i;} else if (HuffmanT[i].parent == -1 && HuffmanT[i].weight < m2)//找出为访问的权值第二小结点{m2 = HuffmanT[i].weight;p2 = i;}}}void CreatHT()//建立哈夫曼树{int i, p1, p2;InitHT();for (i = L.n; i < fn; i++) {SelectMin(i, p1, p2);HuffmanT[p1].parent = HuffmanT[p2].parent = i;HuffmanT[i].lchild = p1;HuffmanT[i].rchild = p2;HuffmanT[i].weight = HuffmanT[p1].weight + HuffmanT[p2].weight;}}int OutArticleCode()//显示文章编码{//文章编码操作ifstream input;input.open(f);if (input.fail()) {cout << "文件不存在!" << endl;return 0;}char ch;cout << "文章编码如下:" << endl;while (!input.eof()) {ch = input.get();for (int i = 0; i < L.n; i++) {if (Code[i].ch == ch)cout << Code[i].bits;}}cout << endl;input.close();}int SaveArticleCode()//保存文章编码{ofstream output;ifstream input;char namef1[max2];input.open(f);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}cout << "请输入保存文章编码的文件名:" << endl;cin >> namef1;output.open(namef1);char ch;while (!input.eof()) {ch = input.get();for (int i = 0; i < L.n; i++) {if (Code[i].ch == ch) {for (int j = 0; j < strlen(Code[i].bits); j++) {output.put(Code[i].bits[j]);}}}}input.close();output.close();cout << "保存完毕!" << endl;}int OutTransCode() {//文章译码操作ifstream input;char namef[max2];cout << "请输入保存文章编码的文件名:" << endl;cin >> namef;input.open(namef);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}char ch;ch = input.get();int c = 2 * L.n - 2;while (!input.eof()) {if (ch == '0')//遇0搜索左子树{if (HuffmanT[c].lchild >= 0) {c = HuffmanT[c].lchild;}if (HuffmanT[c].lchild == -1)//判断是否到叶子{cout << HuffmanT[c].name; //输出字符c = 2 * L.n - 2; //返回根节点}}if (ch == '1')//遇1搜索右子树{if (HuffmanT[c].rchild >= 0) {c = HuffmanT[c].rchild;}if (HuffmanT[c].rchild == -1)//判断是否到叶子{cout << HuffmanT[c].name; //输出字符c = 2 * L.n - 2; //返回根节点}}ch = input.get();}cout << endl;input.close();}int SaveTransCode() {//保存文章译码ofstream output;ifstream input;char namef[max2];char namef1[max2];cout << "请输入文章编码所在的文件名:" << endl;cin >> namef;input.open(namef);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}cout << "请输入保存文章译码的文件名:" << endl;cin >> namef1;output.open(namef1);char ch;ch = input.get();int c = 2 * L.n - 2;while (!input.eof()) {if (ch == '0') {if (HuffmanT[c].lchild >= 0) {c = HuffmanT[c].lchild;}if (HuffmanT[c].lchild == -1) {output.put(HuffmanT[c].name);c = 2 * L.n - 2;}}if (ch == '1') {if (HuffmanT[c].rchild >= 0) {c = HuffmanT[c].rchild;}if (HuffmanT[c].rchild == -1) {output.put(HuffmanT[c].name);c = 2 * L.n - 2;}}ch = input.get();}input.close();output.close();cout << "保存完毕!" << endl;}int FileCompression() {//文件压缩功能CreatHT();CharHuffmanTCoding();//保存编码ofstream output;ifstream input;char namef1[] = {"temp.txt"};input.open(f);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}output.open(namef1);char ch;while (!input.eof()) {ch = input.get();for (int i = 0; i < L.n; i++) {if (Code[i].ch == ch) {for (int j = 0; j < strlen(Code[i].bits); j++) {output.put(Code[i].bits[j]);}}}}input.close();output.close();//压缩文件ofstream File1;ifstream File2;char namef2[max2];cout << "请输入压缩后的文件名:" << endl;cin >> namef2;File1.open(namef2);File2.open(namef1);if (File2.fail()) {cout << "该文件不存在!" << endl;return 0;}char sh;char th;int i = 0;char j = '0';int count = 0;while (!File2.eof()) {sh = File2.get();if (i < 7 && sh != EOF) {count = count + (sh - '0') * pow(2, i);if (sh == '0') {j++;} else {j = '0';}i++;}if (i == 7) {th = count;File1.put(th);i = 0;count = 0;}if (sh == EOF) {th = count;File1.put(th);File1.put(j);i = 0;count = 0;}}File1.close();File2.close();remove(namef1);cout << "文件压缩完毕!" << endl;}int FileDecompression() {//文件解压缩//保存编码fstream output;fstream input;char namef2[max2];char namef1[] = {"temp.txt"};cout << "请输入待解压缩的文件名:" << endl;cin >> namef2;input.open(namef2, ios::in | ios::binary);output.open(namef1, ios::out | ios::binary);if (input.fail()) {cout << "该文件不存在!" << endl;return 0;}char ch;input.read(reinterpret_cast<char*> (&ch), sizeof (ch));char sh;char th = ch;input.read(reinterpret_cast<char*> (&ch), sizeof (ch));int count;char num;char t = '0';char p = '1';while (!input.eof()) {sh = th;th = ch;input.read(reinterpret_cast<char*> (&ch), sizeof (ch));count = sh;if (ch != EOF) {for (int i = 0; i < 7; i++) {num = count % 2 + '0';output.write(reinterpret_cast<char*> (&num), sizeof (num));count = count / 2;}}if (ch == EOF) {for (int i = 0; i < 7; i++) {num = count % 2 + '0';output.write(reinterpret_cast<char*> (&num), sizeof (num));count = count / 2;if (count == 0) {break;}}for (int i = 0; i < th - '0'; i++) {output.write(reinterpret_cast<char*> (&t), sizeof (t));}}}output.close();input.close();//解压文件fstream File1;fstream File2;char namef3[max2];cout << "请输入解压后的文件名:" << endl;cin >> namef3;File2.open(namef1, ios::in | ios::binary);File1.open(namef3);if (File2.fail()) {cout << "该文件不存在!" << endl;return 0;}cout << "解压后的文件内容为:" << endl;File2.read(reinterpret_cast<char*> (&ch), sizeof (ch));int c = 2 * L.n - 2;while (!File2.eof()) {if (ch == '0') {if (HuffmanT[c].lchild >= 0) {c = HuffmanT[c].lchild;}if (HuffmanT[c].lchild == -1) {cout << HuffmanT[c].name;File1.write(reinterpret_cast<char*> (&HuffmanT[c].name), sizeof (HuffmanT[c].name));c = 2 * L.n - 2;}}if (ch == '1') {if (HuffmanT[c].rchild >= 0) {c = HuffmanT[c].rchild;}if (HuffmanT[c].rchild == -1) {cout << HuffmanT[c].name;File1.write(reinterpret_cast<char*> (&HuffmanT[c].name), sizeof (HuffmanT[c].name));c = 2 * L.n - 2;}}File2.read(reinterpret_cast<char*> (&ch), sizeof (ch));}cout << endl;File2.close();File1.close();remove(namef1);cout << "解压完毕!" << endl;}};#endif /* HUFFMANFUNCTION_H */====================================================================== /** File: main.cpp* Author: Administrator** Created on 2011年12月13日, 上午9:09*/#include <iostream>#include "HUFFMANFUNCTION.h"using namespace std;/***/int main(int argc, char** argv){Function *a = new Function;while (1){//主界面显示cout << endl << endl << endl;cout << "<<==================功能选择===============>>" << endl;cout << " 【1】读取文章并对字符编码" << endl;cout << " 【2】哈夫曼编码信息" << endl;cout << " 【3】文章编码" << endl;cout << " 【4】文章译码" << endl;cout << " 【5】压缩文件" << endl;cout << " 【6】解压缩文件" << endl;cout << " 【7】退出程序" << endl;cout << "=========================================================" << endl << endl;char ch;cout << "====>>请选择功能:";cin >> ch;switch (ch){case '1'://读取文章并对字符编码{delete a;a = new Function;system("cls");a->CreatHT();a->CharHuffmanTCoding();cout << "操作完毕!" << endl;system("pause");system("cls");break;}case '2'://哈夫曼编码信息{system("cls");a->OutputHuffmanTCode();system("pause");system("cls");break;}case '3'://文章编码{system("cls");while (1){cout << endl;cout << "========>>1.显示文章编码" << endl;cout << "========>>2.保存文章编码" << endl;cout << "========>>3.返回上一界面" << endl;char ch1;cout << endl << "===>>请选择功能:";cin >> ch1;switch (ch1){case '1'://显示文章编码{system("cls");a->OutArticleCode();system("pause");system("cls");continue;}case '2'://保存文章编码{system("cls");a->SaveArticleCode();system("pause");system("cls");continue;}case '3'://返回上一界面{break;}default:{system("cls");cout << "输入错误,请重新输入!" << endl;continue;}}system("cls");break;}break;}case '4'://文章译码system("cls");while (1){cout << endl;cout << "========>>1.显示文章编码的译码" << endl;cout << "========>>2.保存文章编码的译码" << endl;cout << "========>>3.返回上一界面" << endl;char ch1;cout << endl << "===>>请选择功能:";cin >> ch1;switch (ch1){case '1'://显示文章编码的译码{system("cls");a->OutTransCode();system("pause");system("cls");continue;}case '2'://保存文章编码的译码{system("cls");a->SaveTransCode();system("pause");system("cls");continue;}case '3'://返回上一界面{break;}default:{system("cls");cout << "输入错误,请重新输入!" << endl;continue;}}system("cls");break;}break;case '5':{delete a;a = new Function;system("cls");a->FileCompression();system("pause");system("cls");continue;}case '6':{system("cls");a->FileDecompression();system("pause");system("cls");continue;}case '7':{return 0;}default:{system("cls");cout << "功能选择错误,请重新输入!" << endl;break;}}}return 0;}。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

天津工程师范学院2010届本科生课程设计 天津职业技术师范大学 Tianjin University of Technology and Education

《数据结构》课程设计 哈夫曼编码算法的实现

专 业: 计算机科学与技术 班级学号: 计科0813班 学生姓名: 指导教师: 副教授 系 别:信息技术工程学院

二〇一〇年 六 月 天津工程师范学院2010届本科生课程设计 1课程设计名称 哈夫曼编码算法的实现 2.使用工具软件 Microsoft Visual C++ 天津工程师范学院2010届本科生课程设计 3.课程设计内容简介 3.1目的意义 (1)了解哈夫曼算法的思路核心,掌握哈夫曼算法在压缩算法中的基本应用 (2).全面提高学生的程序设计、开发能力 3.2 基本要求 (1).任意性:用户输入任意的字符串,系统自动给出每个字符的哈夫曼编码和对应的哈夫曼树 (2).友好性:界面要友好,输入有提示,尽量展示人性化 (3).可读性:源程序代码清晰、有层次 (4.)健壮性:用户输入非法数据时,系统要及时给出警告信息 3.3.基本思想 输入字符串及其对应字符权值,记入数组中,进入构造哈夫曼树函数,对所有结点进行初始化将父母和左右孩子域记为空,并利用select函数进行筛选出最小和次小权值,用指针记录,将最小和次小权值相加为其父母的权值进行链接设置,依次构造出哈夫曼树,打印输出;从叶子结点出发到根结点为止编码,以给出的权值为叶子结点的权值来构造哈夫曼树,同时规定哈夫曼树中左分支代表1,右分支代表0,从根结点到每个叶子结点所经过的路径分支上的1或者0组成的字符串为该结点对应字符编码。并循环输出对就字符的编码。 3.4.算法流程 (1)输入给定的n个字符给数组a构成有n个权值的数组w,判断n 的值是否大于2,若小于2提示错误(一个结点构不成哈夫曼树);进入构造哈夫曼树的函数HuffmanTree( )。 (2)计算出总结点数m,并循环将叶子结点和非叶子结点初始化,将叶子结点权值放入相应权值域中,调用select( )函数,选中最小和次小结点的权值,合并成一棵新树,链接父母,最小权值的根结点是新结点的左孩子,次小权值的根结点是新结点的右孩子,将这两个孩子的权值之和作为新树根的权值,输出子树,重复进行直到根结点结束。 (3)进入HuffmanCode( )函数,定义临时存放编码数组cd,并将最后一位放入‘\0’作为结束符,循环得出叶子的每一个编码记为左0右1,记入cd数组,再合并编码成为字符串,复制给hc[i],依次对叶子编码,直到根结点结束,并循环输出编码。结束程序。 天津工程师范学院2010届本科生课程设计 3.5哈夫曼树 3.5.1哈夫曼树 哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的带权路长度记为WPL=(W1*L1+W2*L2+W3*L3+…+Wn*Ln),N个权值Wi(i=1,2,…n)构成一棵N个叶结点的二叉树,相应的叶结点的路径长度为Li(i=1,2,…n)。可以证明哈夫曼树WPL是最小的。 3.5.2构造哈夫曼树的算法 对给定的n个权值{W1,W2,W3,…,Wi,…,Wn}构成n棵二叉树的初始集合F={T1,T2,T3,…,Ti,…,Tn},其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。(为方便在计算机上实现算法,一般还要求以Ti的权值Wi的升序排列。) 在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。 从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。 重复二和三两步,直到集合F中只有一棵二叉树为止。 若用动态的二叉树可用以 3.5.3数据结构: struct tree{ float weight; /*权值*/ union{ char leaf; /*叶结点信息字符*/ struct tree *left; /*树的左结点*/ }; struct tree *right; /*树的右结点*/ }; struct forest{ /*F集合,以链表形式表示*/ struct tree *ti; /* F中的树*/ struct forest *next; /* 下一个结点*/ }; 3.5.4构造哈夫曼树的方法 1) 根据数组a中n个权值建立一棵哈夫曼树,返回树根指针; 2) 动态分配一个由b指向的指针数组; 天津工程师范学院2010届本科生课程设计 3) 初始化b指针数组,使每个指针元素指向a数组中对应元素的结点; 4) 进行n-1次循环建立哈夫曼树; 5) 用s1表示森林中具有最小权值的树根结点的下标,用s2表示森林中具有次最小权值的树根结点的下标; 6) 让s1初始指向森林中第一棵树,s2初始指向森林中第二棵树; 7) 从当前森林中求出最小权值和次最小权值树; 8) 由最小权值树和次最小权值树建立一棵新树,q指向树根结点; 9) 将指向新树的指针赋给b指针数组中s1位置,s2位置置为空; 10) 删除动态建立的数组b; 11) 返回整个哈夫曼树的树根指针。 (a) 在一棵哈夫曼树的生成过程中,每次都由两棵子树构成一棵树,对于n

个叶子结点共需要构成n-1棵子树。所以,在一棵哈夫曼树中只存在双支结点和叶子结点,若叶子结点为n个,则双支结点必为n-1个。 (b) 哈夫曼树最常应用的是哈夫曼编码,哈夫曼编码代表的就是哈夫曼树得

到的字符编码,用0或1表示。 3.5.5 哈夫曼树的例子

给定4个叶子结点① ,②,③ 和④,分别带权7,5,2和4。构造如下图所示的三棵二叉树(还有许多棵),它们的带权路径长度分别为: (c)WPL=7*2+5*2+2*2+4*2=36 (d) WPL=7*3+5*3+2*1+4*2=46 (e) WPL=7*1+5*2+2*3+4*3=35

其中©树的WPL最小,可以验证,它就是哈夫曼树。

(1) ○ / \ ○ ○ 7/ \5 2/ \4 ① ② ③ ④ 天津工程师范学院2010届本科生课程设计 (2) ○ / \2 ○ ③ 4/ \ ④ ○ 7/ \5 ① ② (3) ○ 7/ \ ① ○ 5/ \ ② ○ 2/ \4 ③ ④ 3.6哈夫曼编码 字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5。然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符天津工程师范学院2010届本科生课程设计 的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。为使不等长编码为前缀编码,可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,求出此树的最小带权路径长度就等于求出了传送报文的最短长度。因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编码的条件,又保证报文编码总长最短。 3.7设计步骤

(1).先设计主函数(main函数),然后设计两个函数,分别是: Sree HTelect(HuffmanT,int n) 用来在HT[1..i-1]中选择parent为0且weight最小的两个结点,其序号分别为s1和s2. void HuffmanCoding(HuffmanTree HT, HuffmanCode HC[], int *w, int n)用来哈夫曼树的构造过程,无栈非递归遍历哈夫曼树,求哈夫曼编码. (2) 在主函数(main())里调用HuffmanCoding(HT,HC,w,n);来构造哈夫曼树,在构造哈夫曼树的时候用了slect(HuffmanTree HT,int n)分别选择parent为0且weight最小的两个结点,其序号分别为s1和s2.最后返回到主函数中输出哈夫曼树和哈夫曼编码.

3.8程序结构 天津工程师范学院2010届本科生课程设计 N是否大于1 统计字符串中各个字符的调用HuffmanCoding函数

判断是否能形成哈夫曼

构造哈弗曼树

N

Y

进入编码步骤 定义数组cd 循环从根到叶子求编判断是否为叶子节点

Left N 判断是左右孩子

Y

Right 放1入cd 放0入cd

循环输出编码

返回main函数

结束

一个节点不能构 成哈弗曼树

初始化所有节点 循环创建哈夫曼进入筛选函数选出父母为0且权值最小和次小的结点

将结点值运算赋于 父母孩子结成子树

输出树及建树过

开始 输入字符一串字符

相关文档
最新文档