概念
- 叶子节点的权值:对叶子节点赋予的一个有意义的数值
- 二叉树的带权路径长度:从根节点到叶子节点的路径长度 X 相应叶子节点的权值
- 哈夫曼树:给定一组具有确定权值的叶子节点,可以构造出不同的二叉树,其中带权路径长度最小的二叉树称之为哈夫曼树
设计
-
将单个节点看成是一棵没有parent、lchild、rchild的三叉树,那么权值分别为{w1…wn}可以形成n棵单节点的三叉树森林。
-
每次挑选当前树的根节点权值最小的两棵树,组建成一个新树。因为叶子节点为n,哈夫曼树的总节点数为2n-1。所以需要进行n-1 次的合并。
-
单节点的存储结构为
parent 、lchild、rchild、weight
其中parent、lchild、rchild分别为节点的索引。哈夫曼树的结构为一个连续的数组。(缺点:创建的哈夫曼树规模会被限制。)
typedef struct huffman_node{ int weight; int lchild,rchild,parent; }Element;
算法设计
# include <iostream>
# include <stdlib.h>
# include <stdio.h>
using namespace std;
typedef struct huffman_node{
int weight;
int lchild,rchild,parent;
}Element;
void Select2min(Element * huffnodes,int n,int & m1,int &m2){
for(int i = 0;i < n;i++){
if(huffnodes[i].parent == -1){
m1 = i;
break;
}
}
for(int i = 0;i < n;i ++){
if(huffnodes[i].parent == -1 && huffnodes[i].weight < huffnodes[m1].weight){
m1 = i;
}
}
for(int i = 0;i < n;i++){
if(huffnodes[i].parent == -1 && i != m1){
m2 = i;
break;
}
}
for(int i = 0;i < n;i ++){
if(huffnodes[i].parent == -1 && huffnodes[i].weight < huffnodes[m2].weight && i != m1){
m2 = i;
}
}
cout << "m1 : " << m1 <<" m2 : " << m2 << endl;
}
void InitHuffnodes(Element *huffnodes,int n){
for(int i = 0;i < n;i++){
huffnodes[i].parent = -1;
huffnodes[i].lchild = -1;
huffnodes[i].rchild = -1;
}
}
void HuffmanTree(Element* huffnodes,int * w,int n){
InitHuffnodes(huffnodes,2*n-1);
for(int i = 0;i < n;i++){
huffnodes[i].weight = w[i];
}
for(int t = n;t < 2 * n -1 ;t++){
int m1,m2;
Select2min(huffnodes,t,m1,m2);
huffnodes[m1].parent = t;
huffnodes[m2].parent = t;
huffnodes[t].lchild = m1;
huffnodes[t].rchild = m2;
huffnodes[t].weight = huffnodes[m1].weight + huffnodes[m2].weight;
}
}
void PrintHuffmanTree(Element* huffnodes,int n){
string index = "index";
string weight = "weight";
string parent = "parent";
string lchild = "lchild";
string rchild = "rchild";
printf("%-8s%-8s%-8s%-8s%-8s\n",index.c_str(),weight.c_str(),parent.c_str(),lchild.c_str(),rchild.c_str());
for(int i = 0;i < n;i++){
printf("%-8d%-8d%-8d%-8d%-8d\n",i,huffnodes[i].weight,huffnodes[i].parent,huffnodes[i].lchild,huffnodes[i].rchild);
}
}
int main(){
int w[] = {15,9,7,8,4,11,3,31};
int n = sizeof(w)/sizeof(int);
Element* huffnodes = new Element[2 * n - 1];
HuffmanTree(huffnodes,w,n);
PrintHuffmanTree(huffnodes,2 * n - 1);
return 0;
}
- 打印的结果:
[root@localhost DataStruct]# g++ huffmantree.cc -std=c++11
[root@localhost DataStruct]# ./a.out
m1 : 6 m2 : 4
m1 : 2 m2 : 8
m1 : 3 m2 : 1
m1 : 5 m2 : 9
m1 : 0 m2 : 10
m1 : 11 m2 : 7
m1 : 12 m2 : 13
index weight parent lchild rchild
0 15 12 -1 -1
1 9 10 -1 -1
2 7 9 -1 -1
3 8 10 -1 -1
4 4 8 -1 -1
5 11 11 -1 -1
6 3 8 -1 -1
7 31 13 -1 -1
8 7 9 6 4
9 14 11 2 8
10 17 12 3 1
11 25 13 5 9
12 32 14 0 10
13 56 14 11 7
14 88 -1 12 13
note:可以将其改进写成链表的方式。
文件压缩的设计
- 统计文件A中的各个字符出现的频率
- 以统计出来的频率作为权值,创建哈夫曼树,为每一个字符创建其对应的哈夫曼编码(左为0,又为1的原则)
- 最后一次将文件A中每个字符对应的哈夫曼编码按二进制位保存在文件B中
哈希法:以ascii码为例,将ascii码的数值作为索引,创建节点可以提高查询速度。