导航
1.基本概念,哈夫曼注意点,采用二叉树+贪心
2.采用的方式,自底向上
3.实现代码,及结构体成员,带权路径长度,对于哈夫曼编码长度求解
附:完整代码
————————————————————————————————————————
1.基本概念
构成哈夫曼的两个注意点:
采用的方式:
————————————————————————————————————————
2.采用的方式
例图:
前提要领:
通过使用频率来确定编码的长度,根据频率最小的从下往上
现将每个都作为叶子节点,频率作为权值
做法:
1.选没有双亲,权值最小的两个节点t1,t2
2.t1,t2作为左右子树,来构建一棵新树
3.新树的根权值为t1,t2之和
根据上面的例子,来自己进行画图:
通过此类方法可以得到最小编码
————————————————————————————————————————
3.实现代码,及结构体成员
初始条件:
刚开始有n个对应的字符且有各自权值,就将其看成n个叶子结点,所以我们首先得最先分配2n-1个节点
解释:
结点的结构体:
typedef struct
{
double weight; //权值
int parent; //双亲
int lchild; //左孩子
int rchild; //右孩子
char value; //表示字符
} HNodeType;
首先先给对应分配值:
parent lchild rchild 暂时都设置为-1,权值和对应字符都要补上,并且节点树设置2n-1个
每一次找节点,都要补上五个值,分别为左右孩子,两个孩子的双亲坐标位置,还有就是它的权值
他们的每次双亲值是否为-1代表有没有找过
代码如下:(注意是在循环内的操作)
m1 = m2 = MAXVALUE; //初始化为最大值,m1为最小值,m2为最大值
x1 = x2 = -1; //初始化为-1,存的是对应最小值或者是次小值的下标
//找出所有结点中权值最小、无双亲结点的两个结点
for(j=0;j<n+i;j++){
if(HuffNode[j].weight<m1&&HuffNode[j].parent==-1){ //判断是否有双亲,有的话代表已经分配位置
m2 = m1;
x2 = x1;
m1 = HuffNode[j].weight;
x1 = j; //获取最小值的下标
}
else if(HuffNode[j].weight<m2&&HuffNode[j].parent==-1) {
m2 = HuffNode[j].weight;
x2 = j;
}
}
//填补位置
HuffNode[x1].parent = n+i;
HuffNode[x2].parent = n+i; //将两个最小值的双亲填为要分配的n+i
HuffNode[n+i].weight = m1+m2; //此时分配n+i的权值是是两个最小权值相加
HuffNode[n+i].lchild = x1;
HuffNode[n+i].rchild = x2; //给n+i的左右孩子填上对应最小值,次小值的下标
编码的结构体:
typedef struct
{
int bit[MAXBIT];
int start;
}HCodeType; //存储编码的
哈夫曼树带权路径长度
1.WPL = 每个叶子的权值x该叶子到根的路径长度之和
2.也等于各新生成节点的权值之和
对于哈夫曼编码的长度求解:
每个字符占的空间 = 带权路径长度 / 根结点的权值
若是求等长编码的话求logn的最大值,如果n为6,那么最大为3
————————————————————————————————————————
完整代码:
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
#define MAXBIT 100 //编码数
#define MAXVALUE 10000 //最大值0x3fffffff 或者0x3f3f3f3f memset(a,0x3f,sizeof(a)
#define MAXLEAF 30 //叶子数
#define MAXNODE MAXLEAF*2-1 //总结点
typedef struct
{
double weight; //权值
int parent; //双亲
int lchild; //左孩子
int rchild; //右孩子
char value; //表示字符
}HNodeType; //结点结构体
typedef struct
{
int bit[MAXBIT];
int start;
}HCodeType; //存储编码的
HNodeType HuffNode[MAXNODE]; //定义一个结点结构体数组
HCodeType HuffCode[MAXLEAF]; //定义一个编码结构数组
//构造哈夫曼树
void HuffmanTree(HNodeType HuffNode[MAXNODE],int n)
{
/*i,j循环变量,m1,m2:构造哈夫曼树不同过程中两个最小权值
x1,x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号
*/
int i,j,x1,x2;
double m1,m2;
for(int i=0;i<2*n-1;i++)
{
HuffNode[i].weight = 0; //所有权值为0
HuffNode[i].parent = -1; //双亲都为-1
HuffNode[i].lchild = -1;
HuffNode[i].rchild = -1; //左右孩子都为-1
}
//输入n个叶子结点的权值
for(int i=0;i<n;i++)
{
cout<<"请输入第"<<i+1<<"个叶子结点的value和weight"<<endl;
cin>>HuffNode[i].value>>HuffNode[i].weight;
}
//构造huffman 树
for(int i=0;i<n-1;i++) //执行n-1次合并
{
m1 = m2 = MAXVALUE; //初始化为最大值,m1为最小值,m2为最大值
x1 = x2 = -1; //初始化为-1
//找出所有结点中权值最小、无双亲结点的两个结点
for(j=0;j<n+i;j++){
if(HuffNode[j].weight<m1&&HuffNode[j].parent==-1){
m2 = m1;
x2 = x1;
m1 = HuffNode[j].weight;
x1 = j; //获取最小值的下标
}
else if(HuffNode[j].weight<m2&&HuffNode[j].parent==-1) {
m2 = HuffNode[j].weight;
x2 = j;
}
}
HuffNode[x1].parent = n+i;
HuffNode[x2].parent = n+i; //将两个最小值的双亲填为要分配的n+i
HuffNode[n+i].weight = m1+m2; //此时分配n+i的权值是是两个最小权值相加
HuffNode[n+i].lchild = x1;
HuffNode[n+i].rchild = x2; //给n+i的左右孩子填上对应最小值,次小值的下标
cout<<"x1.weight and x2.weight in round "<<i+1<<"\t"<<HuffNode[x1].value<<" "<<HuffNode[x2].value<<endl;
}
}
//哈夫曼树编码
void HuffmanCode(HCodeType HuffCode[MAXLEAF],int n)
{
HCodeType cd; //定义一个临时变量来存放求解编码时的信息
int i,j,c,p; //c为此时节点的位置,p为c节点中的父亲位置的下标
for(i=0;i<n;i++)
{
cd.start= n-1; //初始位置
c=i; //c为此时节点下标
p = HuffNode[c].parent;
while(p!=-1)
{
if(HuffNode[p].lchild == c)
cd.bit[cd.start] = 0;
else
cd.bit[cd.start] = 1;
cd.start--; //存储编码前移一位
c = p;
p = HuffNode[c].parent; //与上面相同再找父亲的位置
}
//把叶子结点的编码信息从临时编码cd中拿出来,放入编码数组中
for(j=cd.start+1;j<n;j++)
HuffCode[i].bit[j] = cd.bit[j];
HuffCode[i].start = cd.start; //赋值到数组中的初始下标位置
}
}
int main()
{
int i,j,n;
cout<<"Please input n:"<<endl;
cin>>n;
HuffmanTree(HuffNode,n); //构造哈夫曼树
HuffmanCode(HuffCode,n); //哈夫曼树编码
//输出已保存好的所有存在编码的哈夫曼编码
for(i=0;i<n;i++)
{
cout<<HuffNode[i].value<<":Huffman code is:";
for(j=HuffCode[i].start+1;j<n;j++)
cout<<HuffCode[i].bit[j];
cout<<endl;
}
return 0;
}