huffman编码【代码】

哈夫曼编码应该算数据结构“树”这一章最重要的一个问题了,当时大一下学期学的时候没弄懂,一年后现在算是明白了。

首先,讲讲思路。

正好这学期在学算法,这里面就用到了贪心算法,刚好练练手。

整个问题有几个关键点:

1,首先是要思考怎么样存下从txt中读取的一个所有字符中每种字符出现的次数,首先想到的应该是结构体数组,再仔细想想不对呀,时间复杂度太高了,每次判断一个字符都对知道它属于结构体数组中的哪一个,那要是txt中有很多字符,那光这个就得花好多时间。然后想想,如果读取的字符如果只有那255个ASCII中的字符时可以直接用一个整形数组来表示呀!数组的下标为整数,整数和字符数不正是通过ASCII码一一对应了吗。当然这些字符必须全部是那255个中的。所以整形数组的大小也只需255.当然了,如果对C++关联容器的知识比较熟悉,用关联容器更方便,毕竟,我们这种方法其实就是在模拟关联容器。

2,然后我们怎么从刚才得到的各个字符的频率来整出一颗哈夫曼树呢?首先,离散数学上讲的构建哈夫曼树应该是比较容易理解的,就是每次选取两个权值(也就是这里的频率)最小的两个节点作为左右孩子(小的在左,大的在右),然后把它们的权值之和作为一个新的待选择的结点去和剩余结点判断,自底向上构建,直到剩余权值最大的一个结点,完毕。虽然这样说着很简单,但是落实到代码就值得思考了。我们该怎么样表示这样一棵树呢?习惯性地用指针?认真思考后我们发现,用指针行不通,因为这棵树是自底向上构建的,我们在构建完成之前是不知道这棵树的遍历序列的,也就没法通过递归的形式来创建这棵树。那么,我们现在应该想到的是用数组,没错,用数组可以!显然是结构体数组,所以得考虑结构体中要有哪些变量,首先要有每个结点表示的字符,对应的权值,还要有左右孩子的下标,双亲的下标。这样就可以在数组中存下这棵树了。

3,在得到哈夫曼树后,该怎么输出每个字符对应的哈夫曼编码呢?从每个叶子结点出发,逐步向根结点方向遍历,每次到达其双亲时,判断自己是双亲的左孩子还是右孩子,如果是左孩子,在该叶子表示的字符对应的编码(用string表示)加上0,否则加上1。然后再找到双亲的双亲,。。。直到到达根结点(根结点没有双亲,对应的双亲下标可以设为0),由于这样得到的编码01字符串是反的,所以我们要反一下就得到了正确的编码。

这里放一个ppt,是我们老师上课讲的,便于理解 http://files.cnblogs.com/files/journal-of-xjx/huffman.pptx

注意我是把一个txt文件中的所有字符读到一个string中去的。自己测试的时候随便放一个input.txt进去就可以了(注意必须是那255个)

代码如下:

  1 #include <iostream>
  2 #include <fstream>
  3 #include <algorithm>
  4 #include <string.h>
  5 #include <stdlib.h>
  6
  7 using namespace std;
  8
  9 #define NUM_CHARS 256   //读取的字符串中最多出现这么多种字符
 10 #define MAX_FREQ 256   //最大频率必须小于256
 11 #define MAX_SIZE  512
 12 //Huffman Tree结点
 13 typedef struct HuffNode
 14 {
 15     char data;
 16     unsigned int freq;
 17     int parent;
 18     int lchild;
 19     int rchild;
 20 }HuffNode;
 21 //编码结点
 22 typedef struct HuffCode
 23 {
 24     char data;//保存字符
 25     string s;//保存字符对应的编码
 26 }HuffCode;
 27
 28 //给定一个字符串,把字符的出现频率保存到freqs数组中
 29 //(没有出现在字符串中的字符我们不在freqs数组中保存它们为零)
 30 int Create_freq_array(unsigned int (&freqs)[NUM_CHARS],string s, int &char_size)//传入数组的引用
 31 {
 32     int i, maxfreq = 0;
 33     for(int i=0;i<NUM_CHARS;i++)
 34         freqs[i] = 0;//注意传入的数组的各元素先赋值为0
 35     for(auto iter =s.begin(); iter!=s.end(); iter++)
 36     {
 37         freqs[*iter]++;//*iter为char型,这里转换成了int型,即以某个字符的ASCII码作为它在freq数组中的下标,注意这种方式不能表示非ASCII码字符!
 38         if(freqs[*iter] > maxfreq)
 39             maxfreq = freqs[*iter];//每次记得更新maxfreq的值
 40     }
 41     //把字符频率压缩到一个字节。 scaled freqs to (0~255)
 42     if(maxfreq > 0xff)//最大出现频率大于255
 43     {
 44         for(i=0; i<NUM_CHARS; i++)
 45         {
 46             if(freqs[i])
 47             {
 48                 freqs[i] = (int)(freqs[i] * 255.0 / maxfreq + 0.5);//最大频率为255
 49                 if(freqs[i] == 0)//要确保不会被缩小成0!
 50                     freqs[i] = 1;
 51             }
 52         }
 53     }
 54
 55     for(i=0; i<NUM_CHARS; i++)//计算char_size值
 56     {
 57         if(freqs[i])
 58         {
 59             char_size++;
 60         }
 61     }
 62     return 0;
 63 }
 64
 65 //打印字符频率表
 66 int Print_freqs(unsigned int (&freqs)[NUM_CHARS],int n)
 67 {
 68     int i;
 69     char c;
 70     for(i = 0; i < NUM_CHARS; i++)
 71     {
 72         if(freqs[i])
 73         {
 74             c = i;//把i以ASCII码值还原出对应的字符
 75             cout << "字符" << c << "出现的频率为:" << freqs[i] << endl;
 76         }
 77
 78     }
 79     cout << "(注:以上频率均为相对值,并不代表实际字符出现的次数)" <<endl;
 80     cout << endl << "以上共出现" << n << "种字符" << endl <<endl;
 81     return 0;
 82 }
 83
 84 int Build_Huffman_tree(unsigned int (&freqs)[NUM_CHARS],HuffNode (&Huffman_array)[MAX_SIZE],int n)//n表示freqs数组中实际包含的字符种类数
 85 {
 86     char c;
 87     int k = 0,x1,x2;
 88     unsigned int m1, m2;
 89
 90     for(int i=0;i<NUM_CHARS;i++)//把前n个叶结点的信息输入Huffman_array数组
 91     {
 92         if(freqs[i])
 93         {
 94             c=i;//还原字符
 95             Huffman_array[k].data = c;
 96             Huffman_array[k].freq = freqs[i];
 97             Huffman_array[k].parent = 0;
 98             Huffman_array[k].lchild = 0;
 99             Huffman_array[k].rchild = 0;
100             k++;
101         }
102     }
103     for(int i=n;i<2*n-1;i++)//处理剩下n-1个非叶子结点
104     {
105         Huffman_array[k].data = ‘#‘;
106         Huffman_array[k].freq = 0;
107         Huffman_array[k].parent = 0;
108         Huffman_array[k].lchild = 0;
109         Huffman_array[k].rchild = 0;
110     }
111     // 循环构造 Huffman 树
112     for(int i=0; i<n-1; i++)
113     {
114         m1=m2=MAX_FREQ; // m1、m2中存放两个无父结点且结点权值最小的两个结点
115         x1=x2=0;  //x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号
116         /* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */
117         for (int j=0; j<n+i; j++)
118         {
119             if (Huffman_array[j].freq < m1 && Huffman_array[j].parent==0)
120             {
121                 m2=m1;
122                 x2=x1;
123                 m1=Huffman_array[j].freq ;
124                 x1=j;
125             }
126             else if (Huffman_array[j].freq  < m2 &&Huffman_array[j].parent==0)
127             {
128                 m2=Huffman_array[j].freq ;
129                 x2=j;
130             }
131         } /* end for */
132             /* 设置找到的两个子结点 x1、x2 的父结点信息 */
133         Huffman_array[x1].parent  = n+i;
134         Huffman_array[x2].parent  = n+i;
135         Huffman_array[n+i].freq  = Huffman_array[x1].freq  + Huffman_array[x2].freq ;
136         Huffman_array[n+i].lchild = x1;
137         Huffman_array[n+i].rchild = x2;
138     }
139     return 0;
140 }
141 //哈夫曼编码,输出string中各种字符对应的编码
142 int Huffman_code(HuffNode(&Huffman_array)[MAX_SIZE],HuffCode (&Huffman_code_array)[NUM_CHARS],int n)
143 {
144     int temp;
145     for(int i = 0;i < n;i++)
146     {
147         temp = i;//当前处理的Huffman_array数组下标
148         Huffman_code_array[i].data = Huffman_array[i].data;
149         while(Huffman_array[temp].parent)
150         {
151             if(Huffman_array[Huffman_array[temp].parent].lchild == temp)//左孩子为0
152             {
153                  Huffman_code_array[i].s += ‘0‘;
154             }
155             else//右孩子为1
156             {
157                 Huffman_code_array[i].s += ‘1‘;
158             }
159             temp = Huffman_array[temp].parent;
160         }
161         reverse(Huffman_code_array[i].s.begin(), Huffman_code_array[i].s.end());//注意翻转每一个string,这里用到了#include <algorithm>
162     }
163     return 0;
164 }
165
166 int Print_huffman_code(HuffCode (&Huffman_code_array)[NUM_CHARS],int n)
167 {
168     for(int i = 0;i < n;i++)
169     {
170         cout << "字符" << Huffman_code_array[i].data << "对应的哈夫曼编码为:" << Huffman_code_array[i].s << endl;
171     }
172     cout << endl;
173     return 0;
174 }
175
176 int main()
177 {
178     ifstream in("input.txt",ios::in);//从input.txt中读取输入数据
179     string s,temp;
180     int char_size = 0;//用以保存string中所包含的字符种类
181     unsigned int freqs[NUM_CHARS];
182     HuffNode Huffman_array[MAX_SIZE];
183     HuffCode Huffman_code_array[NUM_CHARS];
184     while(getline(in,temp))//按行读取一个txt文件中的各个字符到一个string,注意这种读取方式只有字符之间的空格才会读入,其他空格不读取
185     {
186         s += temp;
187     }
188     cout << "输入的字符总数为: " << s.size() << endl << endl << "其中:" << endl;//string中包含的字符数
189     Create_freq_array(freqs,s,char_size);
190     Print_freqs(freqs,char_size);
191     Build_Huffman_tree(freqs,Huffman_array,char_size);
192     Huffman_code(Huffman_array,Huffman_code_array,char_size);
193     Print_huffman_code(Huffman_code_array,char_size);
194     return 0;
195 }

参考了别人的一些代码。

时间: 2024-12-20 18:12:20

huffman编码【代码】的相关文章

Jcompress: 一款基于huffman编码和最小堆的压缩、解压缩小程序

前言 最近基于huffman编码和最小堆排序算法实现了一个压缩.解压缩的小程序.其源代码已经上传到github上面: Jcompress下载地址 .在本人的github上面有一个叫Utility的repository,该分类下面有一个名为Jcompress的目录便是本文所述的压缩.解压缩小程序的源代码.后续会在Utility下面增加其他一些实用的小程序,比如基于socket的文件断点下载小程序等等.如果你读了此文觉得还不错,不防给笔者的github点个star, 哈哈.在正式介绍Jcompres

基于Huffman编码的压缩软件的Python实现

哈夫曼编码是利用贪心算法进行文本压缩的算法,其算法思想是首先统计文件中各字符出现的次数,保存到数组中,然后将各字符按照次数升序排序,挑选次数最小的两个元素进行连结形成子树,子树的次数等于两节点的次数之和,接着把两个元素从数组删除,将子树放入数组,重新排序,重复以上步骤.为了解压,在压缩时首先往文件中填入huffman编码的映射表的长度,该表的序列化字符串,编码字符串分组后最后一组的长度(编码后字符串长度模上分组长度),最后再填充编码后的字符串.本算法中以一个字节,8位作为分组长度,将编码后二进制

基于二叉树和数组实现限制长度的最优Huffman编码

具体介绍详见上篇博客:基于二叉树和双向链表实现限制长度的最优Huffman编码 基于数组和基于链表的实现方式在效率上有明显区别: 编码256个符号,符号权重为1...256,限制长度为16,循环编码1w次,Release模式下.基于链表的耗时为8972ms,基于数组的耗时为1793ms,速度是链表实现方式的5倍. 详细代码例如以下: //Reference:A fast algorithm for optimal length-limited Huffman codes.pdf,http://p

Huffman编码之文件的解/压缩

问题描述:           生活中文件压缩技术可谓随处可见,在数据的密集型传输中文件压缩是一项重要的实用性技术.例如:较大文件的下载,传输等.常见的文件压缩工具有winRAR,2345好压,快压(KuaiZip)等,这些工具已经开发的相当牛逼,但是作为入门级的程序员来说,不能只停留在观摩的立场上,扮演使用者的角色.有必要深入了解其底层的基础实现方式,掌握基础的文件压缩原理,所以在此将其视为一个小型项目列出,以供大家交流探讨,相互学习.... ★在此之前,先来说说什么是文件压缩,用以抛出一个基

Huffman编码——Java实现

Huffman编码 是一种编码方式,常用于无损压缩.本文只介绍用Java语言来实现该编码方式的算法和数据结构. Huffman编码的核心在于构建一颗最优化的二叉树,首先要得到一个原数据编码中的[编码:频率]的列表,然后根据列表构建二叉树,最后对二叉树编码. 第一步: 计算出每个词(编码)出现的频次,并输出到一个列表 例如字符串:"this is an example of a huffman tree", 它的二进制编码是11101001101000110100111100111000

算法导论之--------------Huffman编码

学习Huffman编码最大的收获是学会了STL中优先队列的使用以及在使用的时候要注意的问题:在使用自定义数据类型的时候,优先队列要重载自己的比较操作符. 关于Huffman树怎么讲解请看算法导论讲解,原理真的很简单,不过要写出完整的代码难点就在于优先队列的使用.不废话了啊,再次强调,想把原理弄清楚,请看算法导论,树上的讲解比网上什么垃圾讲解不知道清晰多少,一看就懂.-----------------终于可以上代码了. //在优先级队列中存入指针类型的节点 #include<iostream>

基于二叉树和双向链表实现限制长度的最优Huffman编码

该代码采用二叉树结合双向链表实现了限制长度的最优Huffman编码,本文代码中的权重全部采用整数值表示.http://pan.baidu.com/s/1mgHn8lq 算法原理详见:A fast algorithm for optimal length-limited Huffman codes.pdf 示例:符号ABCDE的权重分别为10,6,2,1,1 不限制长度的最优Huffman编码为A:0,B:10,C:110,D:1110,E:1111,平均码长为1.8bits/symbol; 限制

Huffman编码实现压缩解压缩

这是我们的课程中布置的作业,找一些资料将作业完成,顺便将其写到博客,以后看起来也方便. 原理介绍 什么是Huffman压缩 Huffman( 哈夫曼 ) 算法在上世纪五十年代初提出来了,它是一种无损压缩方法,在压缩过程中不会丢失信息熵,而且可以证明 Huffman 算法在无损压缩算法中是最优的. Huffman 原理简单,实现起来也不困难,在现在的主流压缩软件得到了广泛的应用.对应用程序.重要资料等绝对不允许信息丢失的压缩场合, Huffman 算法是非常好的选择. 怎么实现Huffman压缩

【POJ1521】【HDU1053】Entropy 哈夫曼(Huffman)编码

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43020921"); } 题意: 输出字符串的长度*8.huffman编码长度.两者比值. 题解: huffman编码: 我们发现对于一个字符串,如果我们把它变成01串,比如ABCDE 那么我们需要 A : 000 B : 001 C : 010 D

Huffman编码(测试源代码)

1.此程序为c++程序 2.以下代码可实现手动输入,即去掉代码中的/*...*/注释符,并同时去掉赋值代码段 3.源代码 #include<iostream> using namespace std; typedef struct { int weight, parent, lchild, rchild; }HTNode,*HuffmanTree; typedef char **HuffmanCode; typedef struct { int weight, locate; }TNode,*