哈弗曼编码——C++实现
2016-07-22 10:24
483 查看
哈夫曼编码是哈夫曼树的一个应用。哈夫曼编码应用广泛,如JPEG中就应用了哈夫曼编码。
首先介绍什么是哈夫曼树。哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的 路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
树的带权路径长度记为WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径
长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。
哈夫曼编码步骤:
一、对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F=
{T1,T2,T3,...,Ti,...,Tn},
其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。(为方便在计算机上实现算法,一般还要求以Ti的权值Wi的升序排列。)
二、在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
三、从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。
四、重复二和三两步,直到集合F中只有一棵二叉树为止。
简易的理解就是,假如我有A,B,C,D,E五个字符,出现的频率(即权值)分别为5,4,3,2,1,那么我们第一步先取两个最小权值作为左右子树构造一个新树,即取1,2构成新树,其结点为1+2=3,如图:
![](https://img-blog.csdn.net/20160722102604871?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
虚线为新生成的结点,第二步再把新生成的权值为3的结点放到剩下的集合中,所以集合变成{5,4,3,3},再根据第二步,取最小的两个权值构成新树,如图:
![](https://img-blog.csdn.net/20160722102622403?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
再依次建立哈夫曼树,如下图:
![](https://img-blog.csdn.net/20160722102642012?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
其中各个权值替换对应的字符即为下图:
![](https://img-blog.csdn.net/20160722102649394?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
所以各字符对应的编码为:A->11,B->10,C->00,D->011,E->010
霍夫曼编码是一种前缀编码。解码时不会混淆。其主要应用在数据压缩,加密解密等场合。
首先介绍什么是哈夫曼树。哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。
所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的 路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。
树的带权路径长度记为WPL= (W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶结点的路径
长度为Li(i=1,2,...n)。可以证明哈夫曼树的WPL是最小的。
哈夫曼编码步骤:
一、对给定的n个权值{W1,W2,W3,...,Wi,...,Wn}构成n棵二叉树的初始集合F=
{T1,T2,T3,...,Ti,...,Tn},
其中每棵二叉树Ti中只有一个权值为Wi的根结点,它的左右子树均为空。(为方便在计算机上实现算法,一般还要求以Ti的权值Wi的升序排列。)
二、在F中选取两棵根结点权值最小的树作为新构造的二叉树的左右子树,新二叉树的根结点的权值为其左右子树的根结点的权值之和。
三、从F中删除这两棵树,并把这棵新的二叉树同样以升序排列加入到集合F中。
四、重复二和三两步,直到集合F中只有一棵二叉树为止。
简易的理解就是,假如我有A,B,C,D,E五个字符,出现的频率(即权值)分别为5,4,3,2,1,那么我们第一步先取两个最小权值作为左右子树构造一个新树,即取1,2构成新树,其结点为1+2=3,如图:
虚线为新生成的结点,第二步再把新生成的权值为3的结点放到剩下的集合中,所以集合变成{5,4,3,3},再根据第二步,取最小的两个权值构成新树,如图:
再依次建立哈夫曼树,如下图:
其中各个权值替换对应的字符即为下图:
所以各字符对应的编码为:A->11,B->10,C->00,D->011,E->010
霍夫曼编码是一种前缀编码。解码时不会混淆。其主要应用在数据压缩,加密解密等场合。
//数据结构——哈夫曼编解码 #include <iostream> #include <cstring> using namespace std; #define MAX 32767 typedef struct { int weight;//结点权值 char value;//结点表示的字符 int parent;//父结点 int lchild; int rchild; }HTNode,*HuffmanTree;//动态分配数组存储霍夫曼树 typedef struct { char *HfmCode;//动态分配数组存储霍夫曼编码 char value; }code,*HuffmanCode; //选择最小权重的两个结点 void select(HuffmanTree &ht,int n,int *s1,int *s2) {//ht为树所在数组的头指针,n为允许查找的最大序号,s1,s2返回最小权重的两个序号 int p1=MAX,p2=MAX;//p1,p2用来记录最小两个权,要求p1<p2 int pn1=0,pn2=0;//pn1,pn2用来记录这两个权的序号 int i; for(i=1;i<=n;i++) { if(ht[i].weight<p1&&ht[i].parent==0) { pn2=pn1; p2=p1; pn1=i; p1=ht[i].weight; }else if(ht[i].weight<p2&&ht[i].parent==0) { p2=ht[i].weight; pn2=i; } } *s1=pn1;//赋值返回 *s2=pn2; } //创建哈夫曼树 void Creat_HuffmanTree(HuffmanTree &ht,int *w,char *st,int n) { int m=2*n-1;//n个叶子节点的树,总结点数为2*n-1 ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元不用 HuffmanTree p;//树结点 int i; w=w+1;//因为w[]的0号单元没用 st=st+1; //初始化叶子结点1-n号 for(p=ht+1,i=1;i<=n;i++,p++,w++,st++) { (*p).weight=*w; (*p).value=*st; (*p).parent=0; (*p).lchild=0; (*p).rchild=0; } //初始化非叶子结点n+1——m for(;i<=m;i++,p++) { (*p).weight=0; (*p).parent=0; (*p).lchild=0; (*p).rchild=0; } int s1,s2;//在select函数中使用,用来存储两个最小权的节点的序号 //创建非叶子结点,建哈夫曼树 for(i=n+1;i<=m;i++) { //在ht[1]~~ht[i-1]的范围内,选择两个parent为0且weight最小的两个结点,序号分别赋值给s1,s2 select(ht,i-1,&s1,&s2); 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; } }//哈夫曼树建立完毕 //输出所有结点权值 void outputHuffman(HuffmanTree &ht,int m) { for(int i=1;i<=m;i++) cout<<ht[i].weight<<" "; } //从叶子结点到根,逆向求每个叶子结点对应的哈弗曼编码 void Creat_HuffmanCode(HuffmanTree &ht,HuffmanCode &hc,int n) { char *cd; int start; int c;//c指向当前结点 int p;//p指向当前结点的父结点 int i; hc=(HuffmanCode)malloc((n+1)*sizeof(code));//分配n个编码的头指针 cd=(char*)malloc(n*sizeof(char));//分配求当前编码的工作空间 cd[n-1]='\0';//从右到左逐位存放编码,首先存放编码结束符 for(i=1;i<=n;i++)//求n个叶子节点对应的哈弗曼编码 { hc[i].value=ht[i].value; start=n-1;//初始化编码起始指针 for(c=i,p=ht[i].parent;p!=0;c=p,p=ht[p].parent)//从叶子结点到根结点求编码 { if(ht[p].lchild==c) cd[--start]='0'; else cd[--start]='1'; } hc[i].HfmCode=(char *)malloc(n*sizeof(char));//为第i个编码分配空间 strcpy(hc[i].HfmCode,&cd[start]); } free(cd); } //哈夫曼解码 void Decoding_HuffmanTree(HuffmanTree &ht,char code[],char result[]) { int i,k=0; int p=0,root; for(root=1;ht[root].parent!=0;root=ht[root].parent) ;//root是哈夫曼树的根 for(i=0,p=root;code[i]!='\0';i++) { if(code[i]=='0') p=ht[p].lchild; else p=ht[p].rchild; if(ht[p].lchild==NULL&&ht[p].rchild==NULL) { result[k++]=ht[p].value; p=root; } } result[k]='\0'; } int main() { HuffmanTree HT; HuffmanCode HC; int *w; //动态数组,存放各字符的权重 char *st; //字符串,存放节点的值 int i,n; //n is the number of elements int m; cout<<"input the total number of the Huffman Tree:"<<endl; cin>>n; w=(int *)malloc( (n+1)*sizeof(int) ); //0号单元不用 st=(char *)malloc( (n+1)*sizeof(char) ); //0号单元不用 FILE *fin=fopen("哈弗曼编码.txt","r"); for(i=1;i<=n;i++) { fscanf(fin,"%c%d",&st[i],&w[i]); } Creat_HuffmanTree(HT,w,st,n); /*构造H树*/ m=2*n-1; outputHuffman(HT,m); /*显示H树*/ cout<<endl; Creat_HuffmanCode(HT,HC,n); /*根据H树,求每个字符的编码,放在HC中*/ for(i=1;i<=n;i++) /*输出编码*/ cout<<HC[i].value<<" "<<HC[i].HfmCode<<endl; //解码 char *code="01101110101010001110110110011100"; char *result; result=(char *)malloc(100*sizeof(char)); Decoding_HuffmanTree(HT,code,result); //result[]存放解码结果 for(i=0;result[i];i++) cout<<result[i]<<" "; return 0; }
相关文章推荐
- 如何写出一个返回多个值的c语言子函数
- 八大排序算法
- C语言pause()函数:让进程暂停直到信号出现
- Matrix67的情书 题解 恺撒移位密码
- 【开发日志】C#/C++传参:注意字符串终止符
- Opencv+C++之身份证识别(一)
- UVA 101 The Blocks Problem
- Opencv+C++之人脸识别二
- Opencv+C++之人脸识别
- C++ 字符串,字符数组,数字之间转换
- POJ 2506 - Tiling
- 无符号整数
- Detected memory leaks!内存泄漏,溢出,内存越界问题分析
- c++ 的输出格式控制
- Leetcode 371. Sum of Two Integers (Easy) (cpp)
- C++中多态的实现原理
- <<More Effective C++>>读书笔记2: 运算符
- POJ 2109 - Power of Cryptography
- C++学习之路(1) 类和对象,命名空间,标准库和std命名空间
- 汉诺塔,递归初步算法。