这个算法用到了去重的算法,构造哈夫曼树的方法。看看我的代码实现吧!
typedef struct{
int quanzhi;
int fu,zuo,you;
}tree;
先定义一个结构题,里面有权值,父亲,左孩子和右孩子。
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].fu=-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].fu==-1){//让m1记最小值,m2记仅次于m1的最小值,并保证没有父亲节点
m2=m1;
x2=x1;
m1=hfm[j].quanzhi;
x1=j;
}
else if(hfm[j].quanzhi<m2&&hfm[j].fu==-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].fu=n+i;
hfm[x2].fu=n+i; }//更新父亲结点,不在加入上面的最小值比较
}
这里加了很多注释,比别人的就有点人性了,我看他们那种没有注释的代码就头大,最后看都不愿看了,还是自己写吧。这个构造哈夫曼树呢一开始我是自己写的代码进行构造的,后来发现太复杂了,然后我去找了哈夫曼树构造方法的视频学习了一下,改进了我的算法,去掉了一些没有必要的东西,这里面最难的应该就是那个找两个最小的结点了,当时我也是卡在这里好一会时间,虽然最后也搞出来了吧,显然没有专业的更简洁。
#include<iostream>
#include<string.h>
using namespace std;
#define max 100
typedef struct{
int quanzhi;
int fu,zuo,you;
}tree;
void vs(char *p,int &n);
void vs(char *p,int &n){ //去重
int i,j,k;
for(i=0;i<n-1;i++) //这里的n必须减一,否则 最后会有一次自己与自己比较,导致表长减一
for(j=i+1;j<n+1;j++) //使用第一个数依次和后面的数进行比较
if(p[i]==p[j])
{
for(k=j;k<n-1;k++){
p[k]=p[k+1]; //如果两个数相等,则把后面的数前移覆盖掉前面的数
}n--;
j--;
if(n==1)break; //数组长度减一
}
}
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].fu=-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].fu==-1){//让m1记最小值,m2记仅次于m1的最小值,并保证没有父亲节点
m2=m1;
x2=x1;
m1=hfm[j].quanzhi;
x1=j;
}
else if(hfm[j].quanzhi<m2&&hfm[j].fu==-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].fu=n+i;
hfm[x2].fu=n+i; }//更新父亲结点,不在加入上面的最小值比较
}
int main(){
char a[max],b[max];//两个数组一个记录原数组,一个记录去重后的数组
int n=0,s[max]; //s数组记录同一字符出现次数
float c,sum=0;
cin>>a;
int al=strlen(a);
for(int i=0;i<al;i++)
b[i]=a[i];
int bl=al;
vs(b,bl); //对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(int i=0;i<bl;i++){ //遍历叶子结点,记录边的个数
int p=i;
while(hfm[p].fu!=-1){
p=hfm[p].fu;
n++;
}
sum+=n*hfm[i].quanzhi;//边数*权值=最后编码时此字符出现的次数
n=0;
}
c=al*8/sum;
cout<<al*8<<" "<<sum<<" ";
printf("%.1f",c);
}
这里是全部的代码,加上注释的代码我相信应该不难看懂,这里只是让求长度,要是求编码的话还得麻烦一步,很多数据结构的学习视频中也有讲到有关的方法与代码实现。
在这里呢我说一下我对做算法题的看法哈!拿到一个算法题,千万不要着急去搜答案,先读题,如果题目超出了你的理解范围,比如这个哈夫曼树,做这个题你首先要知道什么是哈夫曼树,什么是哈夫曼编码,读懂题目之后一定要经过自己的思考,就算做不出来也没关系,但一定要去自己思考一边,这个时间一般是2-3个小时,这么长时间发现自己还是毫无思路或者是卡在一个地方找不到解决的办法,那也不要去固执了,不要一根筋,去看别人的代码,去找视频听讲解,那样会让你焕然一新,你会发出哦!原来如此,你会急不可耐的去写代码,不要去腾别人的代码,那样毫无意义,要自己敲出来,才会印象深刻,要把别人的最优算法记在脑子里,变成自己的东西。