详细图解哈夫曼Huffman编码树

1 引言

  哈夫曼(Huffman)编码算法是基于二叉树构建编码压缩结构的,它是数据压缩中经典的一种算法。算法根据文本字符出现的频率,重新对字符进行编码。因为为了缩短编码的长度,我们自然希望频率越高的词,编码越短,这样最终才能最大化压缩存储文本数据的空间。
  假设现在我们要对下面这句歌词“we will we will r u”进行压缩。我们可以想象,如果是使用ASCII码对这句话编码结果则为:119 101 32 119 105 108 108 32 119 101 32 119 105 108 108 32 114 32 117(十进制表示)。我们可以看出需要19个字节,也就是至少需要152位的内存空间去存储这些数据。
  很显然直接ASCII码编码是很浪费空间的,Unicode就更不用说了,下面我们先来统计一下这句话中每个字符出现的频率。如下表,按频率高低已排序:


这里写图片描述

2 哈夫曼二叉树构建

2.1 初始队列

  那么我们按出现频率高低将其放入一个优先级队列中,从左到右依次为频率逐渐增加。


这里写图片描述

  下面我们需要将这个队列转换成哈夫曼二叉树,哈夫曼二叉树是一颗带权重的二叉树,权重是由队列中每个字符出现的次数所决定的。并且哈夫曼二叉树始终保证权重越大的字符出现在越高的地方。

2.2 第一步合并

  首先我们从左到右进行合并,依次构建二叉树。第一步取前两个字符u和r来构造初始二叉树,第一个字符作为左节点,第二个元素作为右节点,然后两个元素相加作为新空元素,并且两者权重相加作为新元素的权重。


这里写图片描述

  同理,新元素可以和字符i再合并,如下:

这里写图片描述

2.3 重新调整队列

  上图新元素权重相加后结果是变大了,需要对权重进行重新排序。


这里写图片描述

  然后再依次从左到右合并,每合并一次则进行一次队列重新排序调整。如下:

这里写图片描述

  经过多步操作之后,得到以下的哈夫曼二叉树结构,也就是一个带有权重的二叉树:

这里写图片描述

2.4 哈夫曼编码

  有了上面带权重的二叉树之后,我们就可以进行编码了。我们把二叉树分支中左边的支路编码为0,右边分支表示为1,如下图:


这里写图片描述

  这样依次遍历这颗二叉树就可以获取得到所有字符的编码了。例如:‘ ’的编码为10,‘l’的编码为00,‘u’的编码为11100等等。经过这个编码设置之后我们可以发现,出现频率越高的字符越会在上层,这样它的编码越短;出现频率越低的字符越会在下层,编码越短。经过这样的设计,最终整个文本存储空间才会最大化的缩减。
  最终我们可以得到下面这张编码表:

这里写图片描述

2.5 字符串编码

  有了上面的编码表之后,”we will we will r u”这句重新进行编码就可以得到很大的压缩,编码表示为:01 110 10 01 1111 00 00 10 01 110 10 01 1111 00 00 10 11101 10 11100。这样最终我们只需50位内存,比原ASCII码表示节约了2/3空间,效果还是很理想的。当然现实中不是简单这样表示的,还需要考虑很多问题。

3 补充

  我们需要弄明白哈夫曼二叉树概念,它是带权路径达到最小的二叉树,也叫最优二叉树。它不一定是完全二叉树,也不一定是平衡二叉树,它们描述的完全不是一件事情,完全没有概念上的重叠关系。


  个人学习记录,由于能力和时间有限,如果有错误望读者纠正,谢谢!
  转载请注明出处:http://blog.csdn.net/fx677588/article/details/70767446

发布了165 篇原创文章 · 获赞 853 · 访问量 174万+
展开阅读全文

数据结构 哈夫曼编码问题

11-02

#include<string.h> #include<ctype.h> #include<malloc.h> #include<limits.h> #include<stdio.h> #include<stdlib.h> #include<io.h> #include<math.h> #include<process.h> #define TURE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 typedef int Status; typedef int Boolean; typedef struct { unsigned int weight; unsigned int parent,lchild,rchild; }HTNode,*HuffmanTree; //动态分配数组存储哈夫曼树 typedef char **HuffmanCode; //动态分配数组存储哈夫曼编码 void HuffmanCoding(HuffmanTree *HT,HuffmanCode *HC,int *w,int n) { int m,i,s1,s2,start; unsigned c,f; HuffmanTree p; char *cd; if(n<=1) return; m=n*2-1; *HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0號未用 for(p=*HT+1,i=1;i<n;++i,++p,++w) { (*p).weight = *w; //對每個weight初始化 (*p).parent = 0; //對每個parent初始化 (*p).lchild = 0; //對每個lchild初始化 (*p).rchild = 0; //對每個rchild初始化 } for(;i<m;++i,++p) (*p).parent = 0; for(i = n+1;i <= m; ++i) //建哈夫曼樹 { select(*HT,i-1,&s1,&s2); /*在HT[1~i-1]中選擇parent為0 并且 weight 最小的兩個結點,其序號分別是 s1和s2*/ (*HT)[s1].parent = (*HT)[s2].parent =i; (*HT)[i].lchild = s1; (*HT)[i].rchild = s2; (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight; } //求哈夫曼編碼 *HC = (HuffmanCode)malloc((n+1)*sizeof(char*));//分配n個字符編碼 //的頭指針向量【第0個不用】 cd = (char*)malloc(n*sizeof(char));//分配求編碼的工作空間 cd[n-1] = '\0'; //編碼結束符 for(i=1;i<=n;i++) //循環逐個字符求編碼 { start = n-1; //編碼結束符位置 for(c = i,f = (*HT)[i].parent;f!=0;c=f,f=(*HT)[f].parent) { if((*HT)[f].lchild == c) cd[--start] = '0'; else cd[--start] = '1'; } (*HC)[i] = (char*)malloc((n-start)*sizeof(char)); strcpy((*HC)[i],&cd[start]); //複製:從cd輔助編碼(串)到HC } free(cd); //釋放工作空間 } int min(HuffmanTree t,int i) { int j,flag; unsigned int k = UINT_MAX; for(j=1;j<=i;j++) { if(t[j].weight<k && t[j].parent ==0) //循環查找最小的值 k = t[j].weight,flag = j; } t[flag].parent = 1; //對已經查找出來的最小值進行標記 .parent為1 return flag; } void select(HuffmanTree t,int i,int *s1,int *s2) { int j; *s1 = min(t,i); *s2 = min(t,i); if(*s1>*s2) { j = *s1; *s1 = *s2; *s2 = j; //s1為最小的兩個值中序號小的那個 } } void main() { HuffmanTree HT; HuffmanCode HC; printf("請輸入權值的個數(>1):"); int n,i,*w; scanf("%d",&n); w = (int*)malloc(n*sizeof(int)); printf("請依次輸入%d個權值(整數):\n",n); for(i=0;i<=n-1;i++) scanf("%d",w+i); HuffmanCoding(&HT,&HC,w,n); for(i = 1;i<=n; i++) puts(HC[i]); } 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览