导航

1.基本概念,哈夫曼注意点,采用二叉树+贪心
2.采用的方式,自底向上
3.实现代码,及结构体成员,带权路径长度,对于哈夫曼编码长度求解
附:完整代码
————————————————————————————————————————

1.基本概念
树——哈夫曼树基本概念  实现方式 及代码_权值
构成哈夫曼的两个注意点:
树——哈夫曼树基本概念  实现方式 及代码_i++_02
采用的方式:
树——哈夫曼树基本概念  实现方式 及代码_结点_03

————————————————————————————————————————

2.采用的方式
例图:
树——哈夫曼树基本概念  实现方式 及代码_i++_04
前提要领:
通过使用频率来确定编码的长度,根据频率最小的从下往上
现将每个都作为叶子节点,频率作为权值

做法:

1.选没有双亲,权值最小的两个节点t1,t2
2.t1,t2作为左右子树,来构建一棵新树
3.新树的根权值为t1,t2之和

根据上面的例子,来自己进行画图:
树——哈夫曼树基本概念  实现方式 及代码_i++_05
通过此类方法可以得到最小编码

————————————————————————————————————————

3.实现代码,及结构体成员

初始条件:
刚开始有n个对应的字符且有各自权值,就将其看成n个叶子结点,所以我们首先得最先分配2n-1个节点
解释:
树——哈夫曼树基本概念  实现方式 及代码_权值_06

结点的结构体

typedef struct 
{
	double weight;   //权值
	int parent;      //双亲
	int lchild;     //左孩子
	int rchild;     //右孩子
	char value;     //表示字符 
} HNodeType;

首先先给对应分配值
parent lchild rchild 暂时都设置为-1,权值和对应字符都要补上,并且节点树设置2n-1个
树——哈夫曼树基本概念  实现方式 及代码_i++_07

每一次找节点,都要补上五个值,分别为左右孩子两个孩子的双亲坐标位置,还有就是它的权值
他们的每次双亲值是否为-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.也等于各新生成节点的权值之和

树——哈夫曼树基本概念  实现方式 及代码_数据结构_08

对于哈夫曼编码的长度求解
树——哈夫曼树基本概念  实现方式 及代码_数据结构_09
每个字符占的空间 = 带权路径长度 / 根结点的权值
若是求等长编码的话求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;
}