赫夫曼树的原理和构建
1. 赫夫曼树的构造
给定N个权值分别为w1, w2, …, Wn的节点。构造赫夫曼树的算法描述如下:
1)将这N个结点分别作为N棵树仅含一个结点的二叉树,构成森林F.
2)构造一个新节点,并从F中选取两棵根结点权值最小的树作为新节点的
左、右子树,并且将新节点的权值置为左、右子树上根结点的权值之和。
3)从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
4)重复步骤2和3,直至F中只剩下一棵树为止。
2. 赫夫曼编码
哈夫曼编码是一种被广泛应用而且非常有效的数据压缩编码,它是可变长度编码。可变长编码即可以对待处理字符串中不同字符使用不等长的二进制位表示,可变长编码比固定长度编码好很多,可以对频率高的字符赋予短编码,而对频率较低的字符则赋予较长一些的编码,从而可以使平均编码长度减短,起到压缩数据的效果。
哈夫曼编码是前缀编码。如果没有一个编码是另一个编码的前缀,则称这样的编码为前缀编码。对前缀编码的解码也是相对简单的,因为没有一个码是另一个编码的前缀,所以可以识别出第一个编码,将它翻译为原码,再对余下的编码文件重复同样操作。
赫夫曼编码首先要构造一棵赫夫曼树,首先,将每个出现的字符当做一个独立的结点,其权值作为它出现的频度(或次数),然后构造赫夫曼树。显然所有字符节点均出现在叶子结点中。我们可以将字符的编码解释为从根至该字符路径上标记的序列,其中标记为0表示"转向左孩子",标记为1表示为"转向右孩子"。 如下图,矩形方块表示字符及出现的次数。
因此,这棵哈夫曼树的WPL为:
WPL = 1*45 + 3 * (13+12+16) + 4 * (5+9) = 224
代码如下:
haffman.h:
#ifndef _HAFFMANTREE_H
#define _HAFFMANTREE_H
/*
本程序用来实现对赫夫曼编码的最基本的操作
主要步骤:
1 生成一个节点数组用来存放树的节点信息
2 初始化
3 从中找出权值最小和权值次小节点然后生成这两个节点的双亲节点权值最小节点是该双
亲节点的左孩子次小为右孩子然后将该双亲节点加入到节点数组中并标记为已加入
4 重复三步骤知道赫夫曼树构造成功
*/
#include<stdio.h>
#include<malloc.h>
#define LEN_CODE sizeof(HaffmanCode)
#define LEN_NODE sizeof(haffmannode)
#define MAXWEIGHT 1000 //用来存储最大的权值数
#define MaxBits 30 //用来存储转化后的编码
typedef struct HaffManTree
{
int weight; //存放节点的权值
int LeftChild,RightChild,Parent; //采用仿真指针方法存放节点的左右孩子和双亲下标
int flag; //用来判断该节点是否已经加入了赫夫曼树中( 0表示未加入,1表示已经加入 )
}haffmannode;
typedef struct HaffMancode
{
int bits[MaxBits]; //用来存放编码
int weight; //用来存放对应的权值
int start; //存放编码的起始位置
}HaffmanCode;
void MakeTree(int weight[],int n,haffmannode haffmantree[]);
void MakeCode(haffmannode haffmantree[],int n,HaffmanCode haffmancode[]);
void D