重温经典之赫夫曼(Huffman)编码
先看看赫夫曼树
假设有n个权值{w1,w2,…,wn},构造一个有n个叶子结点的二叉树,每个叶子结点权值为wi,则其中带权路径长度WPL最小的二叉树称作赫夫曼树或最优二叉树。
赫夫曼树的构造,赫夫曼最早给出了带有一般规律的算法,俗称赫夫曼算法。如下:
(1)根据给定的n个权值{w1,w2,…,wn}构造n棵二叉树的集合F={T1,T2,…,Tn},其中Ti中只有一个权值为wi的根结点,左右子树为空。
(2)在F中选取两棵根结点的权值为最小的数作为左、右子树以构造一棵新的二叉树,且置新的二叉树的根结点的权值为左、右子树上根结点的权值之和。
(3)在F中删除这两棵树,同时将新得到的二叉树加入到F中。
(4)重复(2)和(3)直到F中只含一棵树为止,这棵树就是赫夫曼树。
例如下图便是赫夫曼树的构造过程。其中,根节点上标注的是所赋的权值。




设计一棵赫夫曼树,由此得到的二进制前缀编码就是赫夫曼编码。那么什么是前缀编码呢?所谓前缀编码,就是若要设计长短不等的编码,则必须是任意一个字符的编码都不是另一个字符编码的前缀。所以我们可以利用二叉树来设计二进制的前缀编码。
假设需要传送的字符为:A B A C C D A。如下图就是一个前缀编码的示例。

http://blog.csdn.net/fengchaokobe/article/details/6969217
说了这么多理论,总该实践一下了,下面是赫夫曼编码的具体实现代码:
- #include <stdio.h>
- #include <string.h>
- #include <malloc.h>
- #include <assert.h>
- #define NUM 256
- typedef struct{
- int weight;
- int parent, lchild, rchild;
- }HTNode, *HuffmanTree;
- /******* Choose two smallest from 0 to n in T *************/
- void Select(HuffmanTree T, int len, int *s1, int *s2)
- {
- int i = 0;
- while (T[i++].parent != -1);
- *s1 = i-1;
- while (T[i++].parent != -1);
- *s2 = i-1;
- if (T[*s1].weight>T[*s2].weight) {
- i = *s1;
- *s1 = *s2;
- *s2 = i;
- }
- for (i=0; i<=len; i++) {
- if(T[i].parent == -1) {
- if (T[*s1].weight > T[i].weight) {
- *s2 = *s1;
- *s1 = i;
- }
- else if (T[*s2].weight >T[i].weight && i != *s1)
- *s2 = i;
- }
- }
- return;
- }
- void show_binary(char ch)
- {
- char i;
- for (i = 0; i < 8; i++) {
- if (ch&0x80)
- printf("1");
- else printf("0");
- if (i == 3)
- printf(",");
- ch <<= 1;
- }
- printf(" ");
- }
- void HuffmanCoding(FILE *psrc, FILE *pdst, FILE *pdeciphering)
- {
- int i;
- char ch;
- int m = 2*NUM-1;
- int size = m*sizeof(HTNode);
- HuffmanTree HT = (HuffmanTree)malloc(size);
- assert(HT);
- memset(HT, -1, size);
- for (i=0; i<NUM; i++)
- HT[i].weight = 0;
- while ((ch=fgetc(psrc)) != EOF) {
- (HT[ch].weight)++;
- }
- rewind(psrc);
- /******************printf the Huffman weight****
- int j;
- for(j=0; j<NUM; j++) {
- printf("%c:%d\t", j, HT[j].weight);
- }
- **********************************************/
- int s1, s2;
- for (i=NUM; i<m; i++) {
- 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;
- }
- /*******************printf the HuffmanTree*********
- int j;
- for (j=0; j<m; j++)
- printf("%d:w%d p%d l%d r%d\t\t", j, HT[j].weight,
- HT[j].parent, HT[j].lchild, HT[j].rchild);
- **************************************************/
- char **HC = (char**)malloc(NUM*sizeof(char*));
- char* cd = (char*)malloc(NUM*sizeof(char));
- cd[NUM-1] = '\0';
- int start,c,f;
- for (i=0; i<NUM; i++) {
- start = NUM-1;
- for (c=i,f=HT[i].parent; f!=-1; c=f,f=HT[f].parent) {
- if (HT[f].lchild==c) cd[--start] ='0';
- else cd[--start] ='1';
- }
- HC[i] = (char *)malloc((NUM-start)*sizeof(char));
- strcpy(HC[i], &cd[start]);
- }
- /************printf the Huffmancode******************************
- int j;
- for (j=0; j<NUM; j++) {
- printf("%c:%s\t", j, HC[j]);
- }
- ****************************************************************/
- char buff[100] = {0};
- char k = 0, j = 0;
- while ((ch=fgetc(psrc)) != EOF) {
- i = -1;
- while (HC[ch][++i] != '\0') {
- buff[j] <<= 1;
- k++;
- if (HC[ch][i] == '1')
- buff[j] |= 0x01;
- if ((k %= 8) == 0)
- j++;
- if (j == 100) {
- j =0;
- fwrite(buff, 1, 100, pdst);
- }
- }
- }
- buff[j] <<= (8-k);
- fwrite(buff, 1, j + 1, pdst);
- /*****************************************************
- printf("\ndata write to %s\n", dstfile);
- for (i=0; i<=j; i++)
- show_binary(buff[i]);
- ***************************************************/
- rewind(pdst);
- fflush(pdst);
- c = 510;
- while (!feof(pdst)) {
- j = fread(buff, 1, 100, pdst);
- /********************************************
- printf("\nfrom read:\n");
- for (i=0; i<j; i++)
- show_binary(buff[i]);
- *******************************************/
- for (i=0; i<j; i++) {
- for (k=0; k<8; k++) {
- if (buff[i]&0x80)
- c = HT[c].rchild;
- else c = HT[c].lchild;
- if (HT[c].lchild == -1) {
- fputc((char)c, pdeciphering);
- c = 510;
- }
- buff[i] <<= 1;
- }
- }
- }
- /**************free the memery and return*******************/
- for(i=0; i<NUM; i++) {
- free(HC[i]);
- }
- free(cd);
- free(HC);
- free(HT);
- HT = NULL;
- fclose(pdst);
- fclose(psrc);
- fclose(pdeciphering);
- return;
- }
- int main(void)
- {
- char srcfile[100], dstfile[100],deciphering[100];
- printf("Input source file:");
- scanf("%s", srcfile);
- printf("Input dest file:");
- scanf("%s", dstfile);
- printf("Input deciphering file:");
- scanf("%s", deciphering);
- FILE *psrc = fopen(srcfile, "r");
- FILE *pdst = fopen(dstfile, "w+");
- FILE *pdeciphering = fopen(deciphering, "w");
- if (psrc == NULL || pdst == NULL || pdeciphering == NULL) {
- printf("file opened failed\n");
- return -1;
- }
- else
- HuffmanCoding(psrc, pdst, pdeciphering);
- return 0;
- }
重温经典之赫夫曼(Huffman)编码的更多相关文章
- puk1521 赫夫曼树编码
Description An entropy encoder is a data encoding method that achieves lossless data compression by ...
- 哈夫曼(Huffman)编码
哈夫曼编码(Huffman Coding)是一种非常经典的编码方式,属于可变字长编码(VLC)的一种,通过构造带权路径长度最小的最优二叉树以达到数据压缩的目的.哈弗曼编码实现起来也非常简单,在实际的笔 ...
- javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题
赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支 ...
- 【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)
参考资料 <算法(java)> — — Robert Sedgewick, Kevin Wayne <数据结构> ...
- C++哈夫曼树编码和译码的实现
一.背景介绍: 给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的 ...
- 【数据结构】赫夫曼树的实现和模拟压缩(C++)
赫夫曼(Huffman)树,由发明它的人物命名,又称最优树,是一类带权路径最短的二叉树,主要用于数据压缩传输. 赫夫曼树的构造过程相对比较简单,要理解赫夫曼数,要先了解赫夫曼编码. 对一组出现频率不同 ...
- Android版数据结构与算法(七):赫夫曼树
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 近期忙着新版本的开发,此外正在回顾C语言,大部分时间没放在数据结构与算法的整理上,所以更新有点慢了,不过既然写了就肯定尽力将这部分完全整理好分享出 ...
- 赫夫曼树JAVA实现及分析
一,介绍 1)构造赫夫曼树的算法是一个贪心算法,贪心的地方在于:总是选取当前频率(权值)最低的两个结点来进行合并,构造新结点. 2)使用最小堆来选取频率最小的节点,有助于提高算法效率,因为要选频率最低 ...
- Java数据结构和算法(四)赫夫曼树
Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...
随机推荐
- postman的安装和使用方法详解
文章来源:http://www.cnplugins.com/tool/specify-postman-methods.html 非官网安装 这是一种直接通过打包已经安装的扩展程序的方式,来进行我认为的 ...
- ES6入门教程---数值扩展和数组扩展
1.数值扩展 var num = 0b11; console.log(num);3 var num = 0o11;console.log(num);9 var num = 1.234; console ...
- php:判断 是否开启 SSL,CURL,ZIP,GD2,MYSQL,是否安装MEMCACHED
对于php的开发环境,通常需要去先判断下一些扩展和服务时不时已经可用~ 看过的欢迎拍砖,给意见~~ <?php /** * 判断 是否开启 SSL,CURL,ZIP,GD2,MYSQL,是否安装 ...
- NSwag生成客户端调用代码
NetCore2.1 WebAPI 根据swagger.json自动生成客户端代码 https://www.cnblogs.com/hunanzp/p/9297361.html 前言 上一篇博客中我们 ...
- c++笔记1
using namespace std:命名空间可以保证一些命名能够在全局不冲突.如用户可以通过声明命名空间,然后用运算符::区别同名的不同变量 using namespace std;namespa ...
- HDU 1087 E - Super Jumping! Jumping! Jumping! DP
http://acm.hdu.edu.cn/showproblem.php?pid=1087 设dp[i]表示去到这个位置时的最大和值.(就是以第i个为结尾的时候的最大值) 那么只要扫描一遍dp数组, ...
- 把一个HashMap的值全部取出来,放到两个数组中
先是从数据库中获取所有的值,返回一个HashMap类型的数据: <pre name="code" class="java"> private Has ...
- java中循环的不同终止方式
1.break:直接强行跳出当前循环,不再执行剩余代码.但在多重循环的情况下,若break在内层循环中,则仅仅终止了内层循环,外循环照常执行. 2.continue:仅仅终止此次循环. 3.retur ...
- HTML5 JSDOM
1,HTML5 新语义化标签 - nav -- 表示导航 - header -- 表示页眉 -- 头部 - section -- 表示区块 -- 类似于div - main -- 文档主要内容 - a ...
- postgresql 存储过程动态插入数据 2
最近学习postgresql,正一个小活要用上,所以就开始学习了!然而,学习的过程极其艰辛,但却也充满了乐趣. 一般来说数据库的操作不外如何增,删,改,查,而首要的就是要添加数据到数据库中,因为以前的 ...