最近又有小伙伴问我哈夫曼的输出问题了,我想了一下,上次写算法只需要求频率,偷懒的我求出频率来就不管了,再三考虑还是再写一下哈夫曼树的输出问题吧,毕竟写个算法要写完整不是吗?来吧走起!
#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两个成员,分别用来记录这个结点时父亲结点的左孩子还是右孩子。
这注释是不是很全啊,我可是竟替你们着想了,还不给我来个三连~