讨论:构建huffman树、huffman编码、计算wpl(略)
构建huffman树的关键问题:
(1)所将要编码的字符作为叶子结点,该字符在文件中的使用频率作为叶子结点的权值,以自底向上的方式,通过n-1次的“合并”运算后构造出的树。核心思想是让权值大的叶子离根最近。
(2)哈夫曼算法采取的贪心策略是每次从树的集合中取出没有双亲且权值最小的两棵树作为左右子树,构造一棵新树,新树根节点的权值为其左右孩子结点权值之和,将新树插入到树的集合中。
树的结构体:
利用数组构建huffman树:
构建huffman编码的关键问题(不等长编码)
(1)编码尽可能的短 使用频率高的字符编码较短,使用频率低的编码较长,可提高压缩率,节省空间,也能提高运算和通信速度。即频率越高,编码越短(也就是叶子离根越近)。
(2)不能有二义性 解决的办法是:任何一个字符的编码不能是另一个字符编码的前缀,即前缀码特性。
叶子结点保存编码:
用例:
首先输入:叶子节点个数。
再输入:节点表示的字符 + 权重(只能为整数)
源码:
#include <bits/stdc++.h>
using namespace std;
int N; //总节点数
int MAXVALUE=100000;
struct Node{
int weight;//权重
int parent;//双亲
int lch;//左孩子
int rch;//右孩子
char value;//该节点表示的字符
}node[100];
//每个叶子节点的编码
struct CodeNode{
int bit[100];
int start;
}codeNode[100];
//初始化叶子节点
void initNode(int n){
for(int i=0;i<n;i++){
cout<<"第"<<i+1<<"叶子初始化:";
cin>>node[i].value>>node[i].weight;
node[i].parent=-1;
node[i].lch=-1;
node[i].rch=-1;
}
}
//构建huffman树
void huftree(int n){
int m1,m2;
int x1,x2;
for(int i=0;i<n-1;i++){//n-1次合并 =2n-1-n
m1=m2=MAXVALUE;//记录权值(m1为最小,m2位次小)
x1=x2=-1;//记录数组下标
for(int j=0;j<n+i;j++){
if(node[j].weight<m1&&node[j].parent==-1){
m2=m1;//将最小变为次小
x2=x1;
m1=node[j].weight;
x1=j;
}else if(node[j].weight<m2&&node[j].parent==-1){
m2=node[j].weight;
x2=j;
}
}
//更新新树信息
node[x1].parent=n+i;//x1的父亲为新节点编号n+i;
node[x2].parent=n+i;//x2的父亲为新节点编号n+i;
node[n+i].weight=m1+m2;
node[n+i].lch=x1;
node[n+i].rch=x2;
node[n+i].parent=-1;
cout<<"新生成节点的下标:"<<node[x2].parent<<",左孩子是"<<x1+1<<",右孩子是"<<x2+1<<",权重为:"<<node[n+i]. weight<<endl;
}
}
//huffman编码(m为当前节点下标,i为待编码叶子下标)
void code(int i,int m){
if(node[m].parent==-1){
return;
}
int k=node[m].parent;//记录父亲节点的下标
if(node[k].lch==m){
codeNode[i].bit[codeNode[i].start]=0;
}else{
codeNode[i].bit[codeNode[i].start]=1;
}
codeNode[i].start++;
code(i,k);
}
//wpl
void wpl(int n){
int total=0;
for(int i=0;i<n;i++){
total=total+node[i].weight*codeNode[i].start;
}
cout<<"带权路径长度为:"<<total<<endl;
}
int main(){
int n;
cin>>n;//叶子个数
N=2*n-1;//总节点数
initNode(n);
huftree(n);
for(int i=0;i<n;i++){
code(i,i);
}
//输出
for(int i=0;i<n;i++){
cout<<node[i].value<<"的编码:";
int m=codeNode[i].start;
for(m=m-1;m>=0;m--){
cout<<codeNode[i].bit[m];
}
cout<<endl;
}
//计算带权路径长度
wpl(n);
return 0;
}