您好,欢迎访问三七文档
哈夫曼编码I.问题描述哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。其压缩率通常在20%~90%之间。哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。II.问题分析对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其它字符代码的前缀。这种编码称为前缀码。设C为编码字符集,则表示其最优前缀码的二叉树中恰有|C|个叶子,|C|-1个内部结点。其中,每个叶子对应于字符集中一个字符。二叉树T的代价:编码文件需要二进制位数使达到最小的前缀码编码方案称为给定编码字符集C的最优前缀码。哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼编码。哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树T。编码字符集中每一字符c的频率是freq。以freq为键值的最小堆优先队列Q用在贪心选择时有效地确定算法当前要合并的2棵具有最小频率的树。一旦2棵具有最小频率的树合并后,产生一棵新的树,其频率为合并的2棵树的频率之和,并将新树插入最小堆优先队列Q。经过n-1次的合并后,优先队列中只剩下一棵树,即所要求的树T。III.算法描述:HUFFMAN(C)//哈夫曼编码算法n=|C|//初始化最小优先队列QINITIALIZE(Q)=BUILD-MIN-HEAP(C)fori=1ton-1allocateanewnodezz.left=x←EXTRACT-MIN(Q)//提取队列Q中的最前列结点z.right=y←EXTRACT-MIN(Q)z.freq←x.freq+y.freqINSERT(Q,z)//在队列Q中插入结点zreturnEXTRACT-MIN(Q)时间复杂度描述:INITIALIZE(Q)=BUILD-MIN-HEAP(C)的时间复杂度为O(n);循环fori=1ton-1的时间复杂度为O(n);其中,z.left=x←EXTRACT-MIN(Q)和z.right=y←EXTRACT-MIN(Q)的时间复杂度分别为O(nlogn);INSERT(Q,z)的时间复杂度为O(nlogn);最后返回最前列结点returnEXTRACT-MIN(Q)的时间复杂度也为O(nlogn)。所以,这个哈弗曼算法的时间复杂度为T(n)=O(nlogn)。().()TcCBTcfreqdcIV.程序#includestdio.h#includestdlib.h#includestring.htypedefstruct//结点结构体{unsignedintweight;//用来存放各个结点的权值unsignedintparent,LChild,RChild;//指向双亲、孩子结点的指针}HTNode,*HuffmanTree;//动态分配数组,存储哈夫曼树typedefchar*HuffmanCode;//动态分配数组,存储哈夫曼编码//选择两个parent为0,且weight最小的结点s1和s2voidSelect(HuffmanTree*ht,intn,int*s1,int*s2){inti,min;for(i=1;i=n;i++){if((*ht)[i].parent==0){min=i;break;}}for(i=1;i=n;i++){if((*ht)[i].parent==0){if((*ht)[i].weight(*ht)[min].weight)min=i;}}*s1=min;for(i=1;i=n;i++){if((*ht)[i].parent==0&&i!=(*s1)){min=i;break;}}for(i=1;i=n;i++){if((*ht)[i].parent==0&&i!=(*s1)){if((*ht)[i].weight(*ht)[min].weight)min=i;}}*s2=min;}//构造哈夫曼树ht,w存放已知的n个权值voidCrtHuffmanTree(HuffmanTree*ht,int*w,intn){intm,i,s1,s2;m=2*n-1;//总共的结点数*ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));for(i=1;i=n;i++)//1--n号存放叶子结点,初始化{(*ht)[i].weight=w[i];(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;}for(i=n+1;i=m;i++)//非叶子结点的初始化{(*ht)[i].weight=0;(*ht)[i].LChild=0;(*ht)[i].parent=0;(*ht)[i].RChild=0;}printf(\n哈夫曼树为:\n);for(i=n+1;i=m;i++)//创建非叶子结点,建哈夫曼树{//在(*ht)[1]~(*ht)[i-1]的范围内选择两个parent为0且weight最小的结点,其序号分别赋值给s1、s2Select(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;printf(%d(%d,%d)\n,(*ht)[i].weight,(*ht)[s1].weight,(*ht)[s2].weight);}printf(\n);}//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码voidCrtHuffmanCode(HuffmanTree*ht,HuffmanCode*hc,intn){char*cd;//定义的存放编码的空间inta[100];inti,start,p,w=0;unsignedintc;hc=(HuffmanCode*)malloc((n+1)*sizeof(char*));//分配n个编码的头指针cd=(char*)malloc(n*sizeof(char));//分配求当前编码的工作空间cd[n-1]='\0';//从右向左逐位存放编码,首先存放编码结束符for(i=1;i=n;i++)//求n个叶子结点对应的哈夫曼编码{a[i]=0;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]='1';//左分支标1a[i]++;}else{cd[--start]='0';//右分支标0a[i]++;}}hc[i]=(char*)malloc((n-start)*sizeof(char));//为第i个编码分配空间strcpy(hc[i],&cd[start]);//将cd复制编码到hc}free(cd);for(i=1;i=n;i++)printf(权值为%d的哈夫曼编码为:%s\n,(*ht)[i].weight,hc[i]);for(i=1;i=n;i++)w+=(*ht)[i].weight*a[i];printf(带权路径为:%d\n,w);}voidmain(){HuffmanTreeHT;HuffmanCodeHC;int*w,i,n,wei;printf(**哈夫曼编码**\n);printf(请输入结点个数:);scanf(%d,&n);w=(int*)malloc((n+1)*sizeof(int));printf(\n输入这%d个元素的权值:\n,n);for(i=1;i=n;i++){printf(%d:,i);fflush(stdin);scanf(%d,&wei);w[i]=wei;}CrtHuffmanTree(&HT,w,n);CrtHuffmanCode(&HT,&HC,n);}
本文标题:哈夫曼编码
链接地址:https://www.777doc.com/doc-6002086 .html