3树-4哈夫曼树

#include<bits/stdc++.h>
#define Maxvalue 20
#define Maxleaf 15
#define Maxnode (2*Maxleaf-1)
using namespace std;
int n=0,len,j,m;
typedef struct{
    int num;         //存放字母的种类
    char ch;         //存放字母的个数
}inf;
inf info[Maxleaf];    	//定义此类型数组,叶子信息leaf information
typedef struct{
    int weight;     	//权值
    int parent;     	//老豆
    int lchild;     	//左孩子
    int rchild;     	//右孩子
}HTnode;
HTnode Hofftree[Maxnode];   //定义此类型数组
typedef struct{
    char cd[10];            //存储每个结点的哈夫曼编码
    int start;              //起始位置
}Hcode;
Hcode hcd[Maxleaf];         //定义此类型数组
void Count(){               //统计字符串中字母总个数以及各个字母的个数
    int k,i;
    char s[Maxvalue];       //定义串
    printf("请输入字符串:\n");scanf("%s",&s);               //输入字符串
    len=strlen(s);printf("字符串字母的个数为:%d\n",len);    //读出长度
    info[0].ch=s[0];                //读取第一个字符
    info[0].num=1;                  //并记录其字符出现次数为1
    for(k=1;k<=len;k++){            //把整个字符串扫一遍
        for(m=0;m<=n;m++)           //n是当前已出现字符个数,info[n]是第n个出现字符的结构体,存种类与次数
            if(info[m].ch==s[k]){   //枚举第M个已出现字符看与当前扫到的SK是否相同
                ++info[m].num;      //s[k]与s[m]是同一个字母则num加1
                break;              //只要扫到与当前字符有重复就可以跳出(这样也算是剪枝吧)
            }//解释上一行,例如dabaca,第一个a计数在下面三行+1,当k是3时SK是第二个a,当M=1扫到第一个A使num++后跳出,如果继续扫到M=5num++就会与当K=5时扫a重复
        if(m>n){                    //对当前sk如果在N+1个字符都没找到,说明有新的字母s[k]出现
            info[++n].ch=s[k];      //存字符
            info[n].num=1;          //存次数
        }
    }
    for(j=0;j<n;j++)printf("字符%c的个数为:%d\n",info[j].ch,info[j].num); //每个出现的字符的种类与次数输出
    for(i=0;i<n;i++)Hofftree[i].weight=info[i].num;                        //第i字母的权值赋予第i节点
}
void CreatHT(HTnode Hofftree[]){//构造哈夫曼树:就是更新每个结点的四个参数
   int lnode,rnode;             //定义左右儿子编号
   double min1,min2;            //定义最大最小值
   for(int i=0;i<=2*n-1;i++)    //全部结点初始化
        Hofftree[i].parent=Hofftree[i].lchild=Hofftree[i].rchild=-1;    //所有结点老豆与左右儿子编号都置初值-1
   for(int i=n;i<2*n-1;i++){    //因为0~n-1的点是叶子,所以n~2n-1是非叶子结点,现在开始构建非叶子结点
       min1=min2=32767;         //最小值与次小值最大化
       lnode=rnode=-1;          //lnode和rnode为最小权值的两个结点位置
       for(int k=0;k<=i-1;k++)  //遍历已构建的结点[0,i-1],一开始仅是叶子结点,之后每轮多加一个非叶子结点
           if(Hofftree[k].parent==-1){      //当前结点老豆为空,即未构造二叉树
               if(Hofftree[k].weight<min1){	//扫到K结点权值比最小值小
                   min2=min1;               //把MIN1赋给MIN2,原最小变次小
                   rnode=lnode;             //左儿变右儿
                   min1=Hofftree[k].weight; //更新MIN1最小值
                   lnode=k;                 //左儿变更为K
               }
               else if(Hofftree[k].weight<min2){    //扫到K结点值小于次小值,就是说在左右子节点权值之间
                   min2=Hofftree[k].weight;         //更新MIN2值
                   rnode=k;                         //右儿子变更为K
               }
           }//以上代码k由0扫到i-1,目的是在尚未构造二叉树的节点中查找权值最小的两个结点,左子节点权值不小于右子节点
       Hofftree[i].weight=Hofftree[lnode].weight+Hofftree[rnode].weight;//i结点权值是左右儿子权值和
       Hofftree[i].lchild=lnode;    //lnode是i左儿
       Hofftree[i].rchild=rnode;    //rnode是i右儿
       Hofftree[lnode].parent=i;    //i是lnode老豆
       Hofftree[rnode].parent=i;    //i是rnode老豆
   }//如此i从n到(2*n-1)-1就把所有非叶子结点构建完成了,而且全部结点三个针都更新完,没有的针就-1
}
void printHT(HTnode Hofftree[]){				        //打印哈夫曼树
    printf("哈夫曼树:\nweight  lchild  rchild  parent\n");//提示语
    for(int i=0;i<2*n-1;i++)printf("%d\t%d\t%d\t%d\n",Hofftree[i].weight,Hofftree[i].lchild,Hofftree[i].rchild,Hofftree[i].parent);
}//有N个字符,即N个叶子,则非叶结点有N-1个,所以树有2N-1个点,依次输出每个结点的权值,左儿,右儿,老豆
void Creathcode(HTnode Hofftree[],Hcode hcd[]){         //求哈夫曼编码
    int f,c;    //当前叶子老豆,当前叶子编号
    Hcode hc;   //开HC临时变量来存,最后再赋给hcd[i],即第i个字符的hcd
    for(int i=0;i<n;i++){       //所有叶子结点扫一次,即所有字符扫一次
        hc.start=n;             //临时变量的开始位置记为n(结合读取输出时可知其实就是空)
        c=i;                    //复制结点编号
        f=Hofftree[i].parent;   //读出当前叶子的老豆
        while(f!=-1){                   //循环直到根节点,根节点的PARENT是-1
            if(Hofftree[f].lchild==c)   //当前节点是老豆结点的左孩子结点
                hc.cd[hc.start--]='0';  //这个结点的最右边的位赋为0(哈夫曼码是由根走到叶,由左走到右的)
            else                        //当前节点是老豆结点的右孩子结点
                hc.cd[hc.start--]='1';  //这个结点的最右边的位赋为0(现在从叶往根走,所以要倒过来)
            c=f;                        //老豆赋给当前结点编号,作为下一轮的儿子
            f=Hofftree[f].parent;       //原老豆的老豆记为当前老豆
        }
        hc.start++;         //因为上面是hc.start--,就是说赋了值之后减了1,但是最后减了1之后那个位没有存值
        hcd[i]=hc;          //临时变量赋给hcd[i],因为i要留下来赋值,所以上面才用了c复制结点编号
    }
    for(int i=0;i<n;i++){                    //扫一次全部字符
        printf("%c的编码为\n",info[i].ch);  //提示语
        for(j=hcd[i].start;j<=n;j++)    //hcd[i].start第i个字符结构体中存的哈夫曼码的开始位置
        printf("%c",hcd[i].cd[j]);      //hcd[i].cd[j]第i个字符结构体中存的哈夫曼码的第J位
        printf("\n");                   //换行
    }
}
int main(){
    Count();                    cout<<endl;//统计所输入字符串中字母总个数以及各个字母的个数
    CreatHT(Hofftree);          cout<<endl;//构造哈夫曼树
    printHT(Hofftree);          cout<<endl;//打印哈夫曼树
    Creathcode(Hofftree,hcd);   cout<<endl;//求哈夫曼编码
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值