哈夫曼编码
来源:互联网 发布:网络销售食品药品 编辑:程序博客网 时间:2024/06/07 00:12
一、问题引入
哈夫曼编码是可变字长编码(VLC)的一种。该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码。
简言之就是,我们的一段话中,每个字母都会有它的出现频率,如果出现频率很高的字母,我们可以用很短的比如0表示,而出现频率很低的字母,我们用10000或者100001之类的编码,可以使得整体的编码长度,大大下降。
并且要注意的是,我们常常会遇到扩展码的情况,就是10000100001这样的,如果这个时候不能保证一个码字不是任何一个码字的前缀码,他就会有歧义,是翻译成10000对,还是翻译成100001对呢?这些问题可以用哈夫曼来解决。
附:很可能得出不同的答案,但是平均编码长度是一样的。
二、算法思想
1.输入并计算词频
2.构建哈夫曼树:算法基本思想是循环的选择具有最低频率的两个节点,生成一颗子数,直至形成数(例题如下:)
3.根据哈夫曼树编码
按输入次序i=0到n寻找对应的树底。P=h[i].parent,再h[p].parent向上寻找,直到顶端
4.根据哈夫曼树译码
按输入的0110101010等次序,从树顶开始往下找,直到达到叶节点,开始又一轮的从树顶开始,这里要注意如果输入到了末尾,却依然没有到达叶节点(树底),直接判错
三、具体代码
#include<iostream>#include<string>#define maxvalue 100000using namespace std;typedef struct{ bool bit[100];//存储编码 int start; //这个节点编码需要多少个bit int weight;//这个节点字符的权重 int parent;//这个节点的父节点 int lchild; int rchild; char ch;//这个节点字符}hnode;void decoded(hnode *tree,string b,int n)//依次读入电文,根据哈夫曼树译码{ int i,j=0;int flag; char endflag='2'; //电文结束标志取2 i=2*n; //从根结点开始往下搜索,2*N是因为(根节点的下标+1)=(2*输入不重复字符数-1),即i+1=2*(n+1)-1 printf("译码后的字符为"); while(b[j]!='2') { flag=0 if(b[j]=='0') i=tree[i].lchild; //走向左孩子 else i=tree[i].rchild; //走向右孩子 if(tree[i].lchild==-1) //tree[i]是叶结点 { flag=1;//本轮到达叶子节点 printf("%c",tree[i].ch); i=2*n; //回到根结点 } j++; } printf("\n"); if(flag==0&&b[j]=='2') //电文读完,但尚未到叶子结点 printf("\nERROR\n"); //输入电文有错}//decodeint huff(hnode *h,int nch)//构建哈夫曼树,注意传进来的n+1,是不同的字符数 { int i,j,m1,m2,x1,x2; for(int i=0;i<2*nch-1;i++) { h[i].parent=-1;h[i].lchild=-1;h[i].rchild=-1; } for(int i=0;i<nch-1;i++)//接下来每一轮的值之前的两者最小值相加,而每一次都会选出两个字符(合并过的父节点或者叶节点都无妨)去合并处理,会发现,最后总共要n-1轮这样的操作 { m1=m2=maxvalue; x1=x2=0; for(int j=0;j<nch+i;j++) { if(h[j].weight<m1&&h[j].parent==-1)//权值小且没有父节点 { m2=m1;//m1虽然当不了最小,但它有可能是次小,值要先保留给m2 x2=x1; m1=h[j].weight; x1=j; } else if(h[j].weight<m2&&h[j].parent==-1) { m2=h[j].weight; x2=j; } } h[x1].parent=nch+i; h[x2].parent=nch+i; h[nch+i].weight=h[x1].weight+h[x2].weight; h[nch+i].lchild=x1; h[nch+i].rchild=x2; }}void code(hnode* h ,int n){ int x=1000; hnode temp;int t,p,j; for(int i=0;i<=n;i++) { temp.start=x;//其实这个初值可以随意,主要是体现栈的思想。因为是从树底下往上一直找到树顶,所以是倒着的存储。 t=i; p=h[t].parent; while(p!=-1) { if(h[p].lchild==t)//如果是父节点的左孩子节点,置0 temp.bit[temp.start]=0; else temp.bit[temp.start]=1;//如果是父节点的右孩子节点,置1 temp.start--; t=p; p=h[t].parent; }//所以temp.bit[j]的范围就是从(最后的temp.start+1到它原来的初值n-1) for(j=temp.start+1;j<=x;j++) h[i].bit[j-temp.start-1]=temp.bit[j]; h[i].start=x-temp.start;//temp.bit[i]有值的范围就是它有几位数 } for(int i=0;i<=n;i++) { cout<<h[i].ch<<" "; for(int j=0;j<h[i].start;j++) cout<<h[i].bit[j]; cout<<endl; }}int input(string str,hnode* h){ cin>>str; int count=0; h[0].ch=str[0]; h[0].weight=1; for(int i=1;i<str.length();i++) { int flag=1; for(int j=0;j<=count;j++) { if(str[i]==h[j].ch) { h[j].weight++; flag=0; break; } } if(flag==1)//如果这是一个全新的字符 { count++; h[count].ch=str[i]; h[count].weight=1; } } return count;//count是最后下标,count+1是有几个不同字符}int main(){ hnode h[1000], temp; int i,j,t,p,n; string str,decode; printf("输入字符串并以回车结尾"); n=input(str,h); huff(h,n+1);//构建哈夫曼树 code(h,n);//根据构建的哈夫曼树 编码 printf("输入发送的编码(以'2'为结束标志)"); cin>>decode; decoded(h,decode,n);//根据构建的哈夫曼树译码}
四、实验结果
0 0
- 信源编码---哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- 哈夫曼编码
- PHP和MySQL开发—第一章
- Ubuntu下安装django
- JavaScript入门学习笔记--(2)
- JAVA中Action层, Service层 ,modle层 和 Dao层的功能区分
- Java Static Variables
- 哈夫曼编码
- linux c之网络编程之TCP(服务器和和客户端基础通信)
- javaBean 的三个标签
- 1.Android日志工具的使用(Log)
- Android事件分发机制案例分析(一)
- Install Java in Linux(Ubuntu/Debian)
- TCP的三次握手和四次挥手
- 用scikit-learn研究局部线性嵌入(LLE)
- 【Github】Github客户端安装方法