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

首先,讲讲思路。

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

整个问题有几个关键点:

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进去就可以了(注意必须是ASCII小于等于255的字符,超过这个范围的字符不能用这种方式表示)

代码如下:

 #include <iostream>
#include <fstream>
#include <algorithm> using namespace std; #define NUM_CHARS 256 //读取的字符串中最多出现这么多种字符
#define MAX_FREQ 10000 //最大频率必须小于这个数
#define MAX_SIZE 512 //Huffman Tree结点
typedef struct HuffNode
{
char data;
unsigned int freq;
int parent;
int lchild;
int rchild;
}HuffNode;
//编码结点
typedef struct HuffCode
{
char data;//保存字符
string s;//保存字符对应的编码
}HuffCode; //给定一个字符串,把字符的出现频率保存到freqs数组中
//注意字符出现的频率不能超出unsigned int所能表示的范围
int Create_freq_array(unsigned int (&freqs)[NUM_CHARS],string s, int &char_size)//传入数组的引用,
{
int i, maxfreq = ;
for(int i=;i<NUM_CHARS;i++)
freqs[i] = ;//注意传入的数组的各元素先赋值为0
for(auto iter =s.begin(); iter!=s.end(); iter++)
{
freqs[*iter]++; //*iter为char型,这里转换成了int型,即以某个字符的ASCII码作为
if(freqs[*iter] > maxfreq)//它在freq数组中的下标,注意这种方式不能表示非ASCII码字符!
maxfreq = freqs[*iter];//每次记得更新maxfreq的值
}
for(i=; i<NUM_CHARS; i++)//计算char_size值
{
if(freqs[i])
{
char_size++;
}
}
return ;
} //打印字符频率表
int Print_freqs(unsigned int (&freqs)[NUM_CHARS],int n)
{
int i;
char c;
for(i = ; i < NUM_CHARS; i++)
{
if(freqs[i])
{
c = i;//把i以ASCII码值还原出对应的字符
cout << "字符 " << c << " 出现的频率为:" << freqs[i] << endl;
} }
cout << endl << "以上共出现" << n << "种字符" << endl <<endl;
return ;
} int Build_Huffman_tree(unsigned int (&freqs)[NUM_CHARS],HuffNode (&Huffman_array)[MAX_SIZE],int n)
{ //n表示freqs数组中实际包含的字符种类数
char c;
int k = ,x1,x2;
unsigned int m1, m2; for(int i=;i<NUM_CHARS;i++)//把前n个叶结点的信息输入Huffman_array数组
{
if(freqs[i])
{
c=i;//还原字符
Huffman_array[k].data = c;
Huffman_array[k].freq = freqs[i];
Huffman_array[k].parent = ;
Huffman_array[k].lchild = ;
Huffman_array[k].rchild = ;
k++;
}
}
for(int i=n;i<*n-;i++)//处理剩下n-1个非叶子结点
{
Huffman_array[i].data = '#';
Huffman_array[i].freq = ;
Huffman_array[i].parent = ;
Huffman_array[i].lchild = ;
Huffman_array[i].rchild = ;
}
// 循环构造 Huffman 树
for(int i=; i<n-; i++)
{
m1=m2=MAX_FREQ; // m1、m2中存放两个无父结点且结点权值最小的两个结点
x1=x2=; //x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号
/* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */
for (int j=; j<n+i; j++)
{
if (Huffman_array[j].freq < m1 && Huffman_array[j].parent==)
{ //如果当前判断的结点的权值小于最小的m1,则把它赋给m1,同时
m2=m1; //更新m1结点的下标, 保持m1是当前所有判断过的元素中是最小的
x2=x1; //再把m1的信息赋给m2,保持m2是当前所有判断过的元素中是第二小的
m1=Huffman_array[j].freq ;
x1=j;
}
else if (Huffman_array[j].freq < m2 &&Huffman_array[j].parent==)
//如果当前判断的结点的权值大于等于最小的m1,但是小于m2,
{ //则只需把它赋给m2,更新m2,保持m2是当前所有判断过的元素中是第二小的
m2=Huffman_array[j].freq ;
x2=j;
}
}
/* 设置找到的两个子结点 x1、x2 的父结点信息 */
Huffman_array[x1].parent = n+i;
Huffman_array[x2].parent = n+i;
Huffman_array[n+i].freq = Huffman_array[x1].freq + Huffman_array[x2].freq ;
Huffman_array[n+i].lchild = x1;
Huffman_array[n+i].rchild = x2;
}
return ;
}
//哈夫曼编码,输出string中各种字符对应的编码
int Huffman_code(HuffNode(&Huffman_array)[MAX_SIZE],HuffCode (&Huffman_code_array)[NUM_CHARS],int n)
{
int temp;
for(int i = ;i < n;i++)
{
temp = i;//当前处理的Huffman_array数组下标
Huffman_code_array[i].data = Huffman_array[i].data;
while(Huffman_array[temp].parent)
{
if(Huffman_array[Huffman_array[temp].parent].lchild == temp)//左孩子为0
{
Huffman_code_array[i].s += '';
}
else//右孩子为1
{
Huffman_code_array[i].s += '';
}
temp = Huffman_array[temp].parent;
}
reverse(Huffman_code_array[i].s.begin(), Huffman_code_array[i].s.end());
} //注意翻转每一个string,这里用到了#include <algorithm>
return ;
} int Print_huffman_code(HuffCode (&Huffman_code_array)[NUM_CHARS],int n)
{
for(int i = ;i < n;i++)
{
cout << "字符 " << Huffman_code_array[i].data << " 对应的哈夫曼编码为:" << Huffman_code_array[i].s << endl;
}
cout << endl;
return ;
} int main()
{
ifstream in("input.txt",ios::in);//从input.txt中读取输入数据
ofstream out("output.txt",ios::out);//向output.txt中写入数据
string s,temp;
int char_size = ;//用以保存string中所包含的字符种类
unsigned int freqs[NUM_CHARS];
HuffNode Huffman_array[MAX_SIZE];
HuffCode Huffman_code_array[NUM_CHARS];
while(getline(in,temp))//按行读取一个txt文件中的各个字符到一个string,每读完一行加上一个'\n'
{
s += temp;
s += '\n';
}
cout << "输入的字符总数为: " << s.size() << endl << endl << "其中:" << endl;//string中包含的字符数
Create_freq_array(freqs,s,char_size);
Print_freqs(freqs,char_size);
Build_Huffman_tree(freqs,Huffman_array,char_size);
Huffman_code(Huffman_array,Huffman_code_array,char_size);
Print_huffman_code(Huffman_code_array,char_size);
return ;
}

参考了别人的一些代码。

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

  1. [老文章搬家] 关于 Huffman 编码

    按:去年接手一个项目,涉及到一个一个叫做Mxpeg的非主流视频编码格式,编解码器是厂商以源代码形式提供的,但是可能代码写的不算健壮,以至于我们tcp直连设备很正常,但是经过一个UDP数据分发服务器之后 ...

  2. 优先队列求解Huffman编码 c++

    优先队列小析      优先队列的模板: template <class T, class Container = vector<T>,class Compare = less< ...

  3. Huffman编码实现电文的转码与译码

    //first thing:thanks to my teacher---chenrong      Dalian Maritime university /* 构造Huffman Tree思路: ( ...

  4. huffman 编码

    huffman压缩是一种压缩算法,其中经典的部分就是根据字符出现的频率建立huffman树,然后根据huffman树的构建结果标示每个字符.huffman编码也称为前缀编码,就是每个字符的表示形式不是 ...

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

    具体介绍详见上篇博客:基于二叉树和双向链表实现限制长度的最优Huffman编码 基于数组和基于链表的实现方式在效率上有明显区别: 编码256个符号,符号权重为1...256,限制长度为16,循环编码1 ...

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

    前言 最近基于huffman编码和最小堆排序算法实现了一个压缩.解压缩的小程序.其源代码已经上传到github上面: Jcompress下载地址 .在本人的github上面有一个叫Utility的re ...

  7. 【算法】Huffman编码(数据结构+算法)

    1.描述 Huffman编码,将字符串利用C++编码输出该字符串的Huffman编码. Huffman树是一种特殊结构的二叉树,由Huffman树设计的二进制前缀编码,也称为Huffman编码在通信领 ...

  8. Huffman编码实现文件的压缩与解压缩。

    以前没事的时候写的,c++写的,原理很简单,代码如下: #include <cstdio> #include <cstdlib> #include <iostream&g ...

  9. Huffman编码(Huffman树)

    [0]README 0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在 理解 "Huffman编码(Huffman树)" 的idea 并用源代码加以实现: 0.2) ...

  10. Huffman编码实现压缩解压缩

    这是我们的课程中布置的作业.找一些资料将作业完毕,顺便将其写到博客,以后看起来也方便. 原理介绍 什么是Huffman压缩 Huffman( 哈夫曼 ) 算法在上世纪五十年代初提出来了,它是一种无损压 ...

随机推荐

  1. php 手动搭建环境

    php手动搭建环境有好多种组合,版本号不一致,会导致搭建失败. 我搭建的组合是: php5.6+MySQL5.6+Apache2.4的组合. 一.PHP语言包下载 首先从官网上下载php5.6 htt ...

  2. Java显式锁学习总结之六:Condition源码分析

    概述 先来回顾一下java中的等待/通知机制 我们有时会遇到这样的场景:线程A执行到某个点的时候,因为某个条件condition不满足,需要线程A暂停:等到线程B修改了条件condition,使con ...

  3. 每天一个linux命令(43)--netstat命令

    netstat 命令用于显示与 IP TCP  UDP 和 ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况.netstat 是在内核中访问网络及相关信息的程序,它能提供TCP连接,T ...

  4. Mac下ssh连接远程服务器时自动断开问题

    在mac下使用securecrt通过ssh连接远程服务器时,总会一段时间没有动作后,ssh被自动断开.在windows下用xmanager貌似没有遇到过这个问题. 在网上找了解决方法如下: 客户端配置 ...

  5. iOS 调试心得

    修复 bug 占用我们日常开发的大部分时间,熟练的使用调试工具可以给我们节约大部分的时间. LLDB 的常用命令 expression expresion 是一个非常常用的命令,我们可以通过这个命令来 ...

  6. 转换器2:ThinkPhp模板转Django模板

    前天写了个<ThinkPhp模板转Flask模板> 居然被同事鄙视了,原因是他用Django,我用Flask,为了避免被他继续安利Django的强大.我决定写一个Django模板转换器. ...

  7. hdoj 1175 (bfs)

    题意: 判断两点之间是否可以通过至多有两次转变方向以达到相连,就是平时玩的连连看游戏,但是不能从外面绕过去. 思路:bfs,给每个加入的队列的点添加转变方向次数turn和点当前要走的方向dir属性,起 ...

  8. casperjs环境安装

    1.python 环境安装 2.PhantomJs安装,戳这里,安装的1.9.8版本的,配置环境变量path:";C:\phantomjs"(注意:安装2.0.0版本,运行casp ...

  9. web从入门开始(5)-----表单

    1.   表单的概念 是用来获取客户端用户数据的(信息)的.如:注册表单,查询表单,登录表单等. 2.  表单的工作原理 1.浏览有表单的网页,填写一些必要的信息,然后单击某个按钮,进行提交. 2.这 ...

  10. Authorization user to use specifical database

    DENY VIEW any DATABASE to PUBLIC;ALTER AUTHORIZATION ON DATABASE::Best TO Best