哈夫曼编码长度及输出

最近又有小伙伴问我哈夫曼的输出问题了,我想了一下,上次写算法只需要求频率,偷懒的我求出频率来就不管了,再三考虑还是再写一下哈夫曼树的输出问题吧,毕竟写个算法要写完整不是吗?来吧走起!

#include<iostream>
#include<string.h>
using namespace std;
#define max 100
typedef struct{
	int quanzhi;
	int zuofu,youfu,zuo,you;
	}tree;
int quchong(char *p,int n,char *a); 
int quchong(char *p,int n,char *a){ //p是原来的数组,n是数组的长度,a是未进行赋值的新数组 ,返回值是新数组的长度 
	a[0]=p[0];
	int l=1;	// 先将第一个值插入,方便比较,不会出现比空的操作 
	int i,j,k;
	for(i=1;i<n;i++)	//这个循环是用来遍历外面数组的 
	{	k=0;
		for(j=0;j<l;j++)
			if(p[i]==a[j]){
				k=1;	//这里用到了一个辅助变量k来验证我们通过比较后是否发现新数组中有重复的元素 
				break;
			}
		if(k==0){
			a[l]=p[i];
			l++;	//再插入一个的同时需要对新数组的长度加一 
		}
		}
			return l;	//返回新数组的长度 
			
}
void hfm_tree(tree *hfm,int *s,int n){		//构建哈夫曼树,传参结构体数组,权值,结点数 
	int i,j,m1,m2,x1,x2;
	for(i=0;i<2*n-1;i++){	//初始化 
		hfm[i].quanzhi=0;
		hfm[i].zuofu=-1;
		hfm[i].youfu=-1;
		hfm[i].zuo=-1;
		hfm[i].you=-1;
	}
	for(i=0;i<n;i++){		//给权值赋值 
		hfm[i].quanzhi=s[i];
	}
	for(i=0;i<n-1;i++){
		m1=m2=9999999;
		x1=x2=-1;
		for(j=0;j<n+i;j++){		//找到权值最小的两颗子树 
			if(hfm[j].quanzhi<m1&&hfm[j].zuofu==-1&&hfm[j].youfu==-1){//让m1记最小值,m2记仅次于m1的最小值,并保证没有父亲节点 
				m2=m1;
				x2=x1;
				m1=hfm[j].quanzhi;
				x1=j;
			}
			else if(hfm[j].quanzhi<m2&&hfm[j].zuofu==-1&&hfm[j].youfu==-1){
				m2=hfm[j].quanzhi;
				x2=j;
			}
		}
		hfm[n+i].quanzhi=hfm[x1].quanzhi+hfm[x2].quanzhi;	//权值等于两颗子树的和,并将左右子树分别指定x1,x2 
		hfm[n+i].zuo=x1;	 
		hfm[n+i].you=x2;
		hfm[x1].zuofu=n+i;
		hfm[x2].youfu=n+i;	//更新父亲结点,不在加入上面的最小值比较 
		}
}
int main(){
	char a[max],b[max];//两个数组一个记录原数组,一个记录去重后的数组 
	int n=0,s[max],i,j;	
	float c,sum=0;//s数组记录同一字符出现次数 
	string h[max],z="0",y="1";
	cin>>a;
	int al=strlen(a);
	int bl=quchong(a,al,b);	//对b数组去重 
	for(int i=0;i<bl;i++)
		{for(int j=0;j<al;j++)
			if(b[i]==a[j])
				n++;
			s[i]=n;
			n=0;
		}
		tree hfm[max];
		hfm_tree(hfm,s,bl);	//构造哈夫曼树 
		for(i=0;i<bl;i++){
			int p=i;
			while(!(hfm[p].zuofu==-1&&hfm[p].youfu==-1)){	//当遍历到左右父亲都没有的时候就代表我们找到了根节点 
			if(hfm[p].youfu==-1){
				 h[i]=z+h[i];	//使用了字符串拼接,用了一个字符串的数组来记录 
				p=hfm[p].zuofu;
			}
			else
			{
				 h[i]=y+h[i];
				p=hfm[p].youfu;
			}
		}
		}
		for(i=0;i<al;i++){	//这是一个原来数组遍历的过程,然后输出对应的编码 
			for(j=0;j<bl;j++){
				if(a[i]==b[j]){
					cout<<h[j];	 
					break;
				}
			}
		}
		for(int i=0;i<bl;i++){	//遍历叶子结点,记录边的个数 
			int p=i;
			while(!(hfm[p].zuofu==-1&&hfm[p].youfu==-1)){
			if(hfm[p].youfu==-1){
				p=hfm[p].zuofu;
			}
			else
			{
				p=hfm[p].youfu;
			}
				n++;
			}
			sum+=n*hfm[i].quanzhi;//边数*权值=最后编码时此字符出现的次数 
			n=0;
		}
		cout<<endl<<sum;	//这是长度 
	
}

在这里插入图片描述
这里我用的方法可能不是很标准,但是做出来不就行了,方法都是人想的,我和别人的不一样才显得突出对吧,这里我自主的加上了一个zuofu和youfu两个成员,分别用来记录这个结点时父亲结点的左孩子还是右孩子。
这注释是不是很全啊,我可是竟替你们着想了,还不给我来个三连~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值