哈夫曼树(Huffman Tree):
给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
哈夫曼编码
在数据通信中,需要将传送的文字转换成二进制的字符串,用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(28=256)的频率值以2-4BYTES的长度顺序存储起来,(用4Bytes的长度存储频率值,频率值的表示范围为0–232-1,这已足够表示大文件中字符出现的频率了)以便解压时创建同样的哈夫曼树进行解压;第二遍则根据第一遍扫描得到的哈夫曼树进行编码,并把编码后得到的码字存储起来。
哈夫曼动态编码:
动态哈夫曼编码使用一棵动态变化的哈夫曼树,对第t+1个字符的编码是根据原始数据中前t个字符得到的哈夫曼树来进行的,编码和解码使用相同的初始哈夫曼树,每处理完一个字符,编码和解码使用相同的方法修改哈夫曼树,所以没有必要为解码而保存哈夫曼树的信息。编码和解码一个字符所需的时间与该字符的编码长度成正比,所以动态哈夫曼编码可实时进行。
哈夫曼译码
在通信中,若将字符用哈夫曼编码形式发送出去,对方接收到编码后,将编码还原成字符的过程,称为哈夫曼译码。
基本术语
路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
哈夫曼树实现
分为四步:
(1)初始化:根据给定的n个权值,构造n棵二叉树的森林集合F={T1,T2,…,Tn},其中每棵二叉树只有一个权值为Wi的根节 点,左右子树均为空。
(2)找最小的树并构造新的树:在森林集合F中选取两颗根的权值最小的树作为左右子树构造一棵新的二叉树,新二叉树的根结点为新增加的结点,其权值为左右子树根的权值之和。
(3)删除与插入:在森林集合中删除已选取的两棵根的权值最小的树,同时将新构造的二叉树加入到森林集合F中。
(4)重复(2)和(3)步骤:直到森林集合只含有一棵树为止,这棵树即为哈夫曼树。
#include<iostream>
#include<vector>
using namespace std;
typedef int DataType;
struct HTreeNode
{
DataType data;
HTreeNode* leftChild;
HTreeNode* rightChild;
};
class HuffTree
{
HTreeNode * root;
int len;
public:
HuffTree();
~HuffTree();
void creatHuffTree(DataType arr[], int n);
void githuffcode();
void githuffcode(HTreeNode*HBT, int n);
private:
};
HuffTree::HuffTree()
{
root = NULL;
len = 0;
}
HuffTree::~HuffTree()
{
}
void HuffTree::creatHuffTree(DataType arr[], int n)
{
HTreeNode** b;
HTreeNode* q=NULL;
b = new HTreeNode*[n];
int i, j;
for ( i = 0; i < n; i++)
{
b[i]=new HTreeNode;
b[i]->data = arr[i];
b[i]->leftChild = NULL;
b[i]->rightChild = NULL;
}
for ( i = 1; i < n; i++)
{
int k1 = -1;
int k2;
for (j = 0; j < n; j++)
{
if (k1 == -1 && b[j] != NULL)
{
k1 = j;
continue;
}
if (b[j] != NULL)
{
k2 = j; break;
}
}
for (j = k2; j < n; j++)
{
if (b[j] != NULL){
if (b[j]->data < b[k1]->data)
{
k2 = k1; k1 = j;
}
else if (b[j]->data < b[k2]->data)
{
k2 = j;
}
}
}
q = new HTreeNode;
q->data = b[k1]->data + b[k2]->data;
q->leftChild = b[k1];
q->rightChild = b[k2];
b[k1] = q;
b[k2] = NULL;
}
delete[]b;
root = q;
}
void HuffTree::githuffcode()
{
githuffcode(root,len);
}
void HuffTree::githuffcode(HTreeNode* BHT, int len)
{
static int arr[15];
if (BHT != NULL)
{
if (BHT->leftChild==NULL&&BHT->rightChild==NULL)
{
cout << "结点" << BHT->data << "的哈夫曼编码为" << endl;
for (int i = 0; i < len; i++)
{
cout << arr[i];
}
cout << endl;
}
else{
arr[len] = 0;
githuffcode(BHT->leftChild, len + 1);
arr[len] = 1;
githuffcode(BHT->rightChild,len + 1);
}
}
}
int main()
{
DataType arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
HuffTree HFT;
HFT.creatHuffTree(arr,10);
HFT.githuffcode();
return 0;
}
结果
树形结构图
我要睡觉了(电脑画图太慢了=。=)
画的太丑不要怪我!