作者:冯建,华清远见嵌入式培训中心讲师。
一、简介
哈夫曼树又称为最优树。
1、路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或子孙结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
2、结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL
二、哈夫曼树的应用
1、哈夫曼编码
在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA EAR ARE ART AREA”,这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5。然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码。
为使不等长编码为前缀编码(即要求一个字符的编码不能是另一个字符编码的前缀),可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,求出此树的最小带权路径长度就等于求出了传送报文的最短长度。因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编码的条件,又保证报文编码总长最短。
哈夫曼静态编码:它对需要编码的数据进行两遍扫描:第一遍统计原数据中各字符出现的频率,利用得到的频率值创建哈夫曼树,并必须把树的信息保存起来,即把字符0-255(2^8=256)的频率值以2-4BYTES的长度顺序存储起来,(用4Bytes的长度存储频率值,频率值的表示范围为0--2^32-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。
哈夫曼动态编码:动态哈夫曼编码使用一棵动态变化的哈夫曼树,对第t+1个字符的编码是根据原始数据中前t个字符得到的哈夫曼树来进行的,编码和解码使用相同的初始哈夫曼树,每处理完一个字符,编码和解码使用相同的方法修改哈夫曼树,所以没有必要为解码而保存哈夫曼树的信息。编码和解码一个字符所需的时间与该字符的编码长度成正比,所以动态哈夫曼编码可实时进行。[3]
2、哈夫曼译码
在通信中,若将字符用哈夫曼编码形式发送出去,对方接收到编码后,将编码还原成字符的过程,称为哈夫曼译码。[4]
三、二叉树实现哈夫曼编码
/*************************
Filename :Linkqueue.h
**************************/
typedef struct _tree_
{
char ch;
int weight;
char code[10];
struct _tree_ *next, *lchild, *rchild;
} bitree;
typedef bitree *datatype;
typedef struct _node_
{
datatype data;
struct _node_ *next;
} listnode;
typedef struct
{
listnode *front;
listnode *rear;
} linkqueue;
linkqueue *CreateEmptyLinkqueue();
int EmptyLinkqueue(linkqueue *q);
void EnQueue(linkqueue *q, datatype x);
datatype DeQueue(linkqueue *q);
/*************************
Filename :Huffman.c
**************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linkqueue.h"
bitree *CreateEmptyList()
{
bitree *h;
h = (bitree *)malloc(sizeof(bitree));
h->next = h->lchild = h->rchild = NULL;
return h;
}
void InsertList(bitree *h, bitree *q)
{
while (h->next && (h->next->weight < q->weight))
{
h = h->next;
}
q->next = h->next;
h->next = q;
return;
}
void VisitList(bitree *h)
{
h = h->next;
while ( h )
{
printf("%d ", h->weight);
h = h->next;
}
printf("\n");
return;
}
void CreateHaffmanTree(bitree *h)
{
bitree *p;
while (h->next && h->next->next)
{
p = (bitree *)malloc(sizeof(bitree));
p->lchild = h->next;
p->rchild = h->next->next;
p->weight = p->lchild->weight + p->rchild->weight;
h->next = p->rchild->next;
InsertList(h, p);
}
return;
}
void NoOrder(bitree *root)
{
bitree *p;
linkqueue *queue;
queue = CreateEmptyLinkqueue();
EnQueue(queue, root);
while ( !EmptyLinkqueue(queue) )
{
p = DeQueue(queue);
if ((p->lchild == NULL) && (p->rchild == NULL))
{
printf("%c[%2d] : %s\n", p->ch, p->weight, p->code);
continue;
}
if (p->lchild)
{
strcpy(p->lchild->code, p->code);
strcat(p->lchild->code, "0");
EnQueue(queue, p->lchild);
}
if (p->rchild)
{
strcpy(p->rchild->code, p->code);
strcat(p->rchild->code, "1");
EnQueue(queue, p->rchild);
}
}
return;
}
int main(int argc, char *argv[])
{
int i, value;
char ch;
bitree *h, *p;
h = CreateEmptyList();
while (scanf("%c %d\n", &ch, &value) == 2)
{
p = (bitree *)malloc(sizeof(bitree));
p->ch = ch;
p->weight = value;
p->code[0] = '\0';
p->lchild = p->rchild = NULL;
InsertList(h, p);
}
VisitList(h);
CreateHaffmanTree(h);
NoOrder(h->next);
return 0;
}
/*************************
Filename :Linkqueue.c
**************************/
#include <stdio.h>
#include <stdlib.h>
#include "linkqueue.h"
linkqueue *CreateEmptyLinkqueue()
{
linkqueue *q;
q = (linkqueue *)malloc(sizeof(linkqueue));
q->front = q->rear = (listnode *)malloc(sizeof(listnode));
q->front->next = NULL;
return q;
}
int EmptyLinkqueue(linkqueue *q)
{
return (q->front == q->rear);
}
void EnQueue(linkqueue *q, datatype r)
{
listnode *p;
p = (listnode *)malloc(sizeof(listnode));
p->data = r;
p->next = NULL;
q->rear->next = p;
q->rear = p;
return;
}
datatype DeQueue(linkqueue *q)
{
listnode *p;
p = q->front;
q->front = p->next;
free(p);
return q->front->data;
}
嵌入式及3G相关资源及学习请点击:嵌入式开发视频 android开发视频 android培训 3G培训 QT培训 QT开发视频 物联网培训 物联网技术视频 嵌入式学习