哈夫曼编码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<conio.h>
#define N 1000
#define M 2*N-1///N个叶子结点生成2*N-1的结点
typedef char* huffmanCode[2*M];///哈夫曼编码
typedef struct///哈夫曼树结构体
{
    int weight;///权值
    int parent;///父节结点
    int lchild;///左孩子
    int rchild;///右孩子
}huffman[M+1];
typedef struct Node
{
    int weight; ///叶子结点的权值
    int len; ///叶子结点的二进制码的长度
    char ch; ///叶子结点
}leafcode[N];
void creat_leaf(char *,int *,leafcode ,int *);///创建叶子结点
void creat_huffmantree(huffman ,leafcode ,int );///创建哈夫曼树
void creat_leafcode(huffman ,char *,huffmanCode ,leafcode ,int ,int );///创建叶子结点的编码
void All_code(char *,huffmanCode ,huffmanCode ,leafcode ,int ,int );///所有的字符的编码
void transform_huffman(huffman ,leafcode ,huffmanCode ,int ,int );///解码
void clear();///清屏
int main()
{
    int choose;
    int i,n=0,m=0;///n为叶子结点的个数,m为字符串ch[]的长度
    char str[N]; ///输入的字符串
    huffman Tree; ///哈夫曼树
    huffmanCode leaf_code,all_code; ///leaf_code存放叶子结点的编码,all_code存放所有结点的编码
    leafcode leaf_information; ///存放叶子结点的信息
    do{
        printf("\t\t\t**********************\n");
        printf("\t\t\t*1.产生叶子结点信息  *\n");
        printf("\t\t\t*2.创建哈夫曼树      *\n");
        printf("\t\t\t*3.创建叶子结点的编码*\n");
        printf("\t\t\t*4.译码              *\n");
        printf("\t\t\t*0.退出              *\n");
        printf("\t\t\t**********************\n");
        printf("\t\t\t请输入你需要操作的数字:");
        scanf("%d",&choose);
        if(choose==0)
        {
            printf("\t\t\t欢迎下次再使用!\n");
            break;
        }
        switch(choose)
        {
        case 1: printf("请输入一段字符串:");
                getchar();///回车键会被gets吸收,所以添加一个getchar来吸收回车键
                gets(str);
                for(i=0;str[i]!='\0';i++)
                    if(str[i]>='A'&&str[i]<='Z')
                        str[i]+=32;
                puts("字符串转小写:");puts(str);
                 creat_leaf(str,&m,leaf_information,&n);///产生叶子结点信息,m为字符串str的长度
                    puts("字符:权值");
                        for(i=1;i<=n;i++) ///输出叶子结点的字符与权值
                            printf("   %c:%d \n",leaf_information[i].ch,leaf_information[i].weight);
                break;
        case 2:creat_huffmantree(Tree,leaf_information,n); ///产生哈夫曼的树
            puts("\torder\tweight\tparent\tlchild\trchild");
            for(i=1;i<=2*n-1;i++) ///打印哈夫曼树的信息
                    printf("\t%d\t%d\t%d\t%d\t%d\n",i,Tree[i].weight,Tree[i].parent,Tree[i].lchild,Tree[i].rchild);
            break;
        case 3:creat_leafcode(Tree,str,leaf_code,leaf_information,m,n); ///创建叶子结点的编码
            puts("字符:编码");
            for(i=1;i<=n;i++)///打印每个字符的编码
                printf("   %c:%s\n",leaf_information[i].ch,leaf_code[i]);
            break;
        case 4:All_code(str,leaf_code,all_code,leaf_information,n,m);///所有字符的编码都储存到all_code里,等待输出
            puts("字符串的编码:");
            for(i=0;i<m;i++)///输出刚才输入的字符串的编码
                printf("%s",all_code[i]);
            puts("\n解码如下:");
            transform_huffman(Tree,leaf_information,all_code,n,m); ///解码
            puts("");
            break;
        default:printf("输入错误,请重新输入\n");break;
        }
        clear();
    }while(1);
    return 0;
}
///产生叶子结点的字符和权值
void creat_leaf(char *ch,int *s,leafcode leaf_information,int *p)
{
    int i,j,k;
    int tag;
    *p=0;///叶子节点个数
    for(i=0;ch[i]!='\0';i++) ///统计字符出现个数,放入leaf_information
    {
        tag=1;///做标记
        for(j=0;j<i;j++)///这个是循环是,第一次出现的字符已经把它相关的权值弄好了,然后去匹配它前面的如果有相同的话不再需要去计算它的权值了
            if(ch[j]==ch[i])///如果有相同的就不需要再操作下面的
            {
                tag=0;
                break;
            }
        if(tag)
        {
            leaf_information[++*p].ch=ch[i];///把这个字符储存到结构体数组
            leaf_information[*p].weight=1;///进入这个条件说明是第一次进来,那么就初始化为1,也就是它本身自己
            for(k=i+1; ch[k]!='\0'; k++)///去匹配这个字符的下个字符到结束为止出现了几次
                if(ch[i]==ch[k])
                    leaf_information[*p].weight++;///如果有相同的话,权值就自增1
        }
    }
    *s=i;///字符串长度
    ///操作完,也就把字符串的每个字符的信息都整理好了
}
///创建哈夫曼树
void creat_huffmantree(huffman ht,leafcode leaf_information,int n)
{
    int i,j;
    int s1,s2;
    for(i=1;i<=n;i++)///初始化哈夫曼树
    {
        ht[i].weight=leaf_information[i].weight;
        ht[i].parent=0;
        ht[i].lchild=0;
        ht[i].rchild=0;
    }
    for(i=n+1;i<=2*n-1; i++)///初始化即将使用的哈夫曼树结点
    {
        ht[i].weight=0;
        ht[i].parent=0;
        ht[i].lchild=0;
        ht[i].rchild=0;
    }
    for(i=n+1;i<=2*n-1; i++)///开始合并
    {
        for(j=1;j<=i-1;j++)
            if(!ht[j].parent)
                break;
        s1=j; ///找到第一个双亲为零的结点
        for(;j<=i-1;j++)///找到第一个双亲为0的结点不代表它是最小的,再循环一遍去找到最小的
            if(!ht[j].parent)
                s1=ht[s1].weight>ht[j].weight?j:s1;///三目运算符,如果比之前那个结点小,那就更新s1
        ht[s1].parent=i;///最小结点的父亲结点
        ht[i].lchild=s1;///那么它就是父亲结点的左孩子,因为左孩子是比右孩子小的
        for(j=1;j<=i-1;j++)///再找第二个,第二个也是以此类推的
            if(!ht[j].parent)
                break;
        s2=j; ///找到第二个双亲为零的结点
        for(;j<=i-1;j++)
            if(!ht[j].parent)
                s2=ht[s2].weight>ht[j].weight?j:s2;
        ht[s2].parent=i;
        ht[i].rchild=s2;
        ht[i].weight=ht[s1].weight+ht[s2].weight;///找它父亲结点的两个孩子之后进行相加,也就是这两个叶子结点的权值
    }
    ///操作完之后也就是是哈夫曼树创建完成
}
///创建叶子结点的编码
void creat_leafcode(huffman ht,char *ch,huffmanCode leaf_code,leafcode leaf_information,int m,int n)
{
    int i,p,c,start;
    char *code_temp;///临时的编码
    code_temp=(char *)malloc(n*sizeof(char));///申请空间
    code_temp[n-1]='\0';///末尾置0
    for(i=1;i<=n;i++)
    {
        start=n-1; ///code_temp串每次从末尾开始
        c=i;///叶子结点
        p=ht[i].parent;///p在n+1到2n-1,p是叶子结点的父亲
        while(p) ///沿父亲结点方向开始遍历,直到为0才结束,也就是我们会从底搜到最上面,这也是哈夫曼的核心思想
        {
            start--;///依次向前
            if(ht[p].lchild==c)///如果这个父亲的左孩子是这个叶子结点,就赋值0
                code_temp[start]='0';
            else ///否则赋值1
                code_temp[start]='1';
            c=p;///保存这个父亲的结点的值,用在下一次的匹配
            p=ht[p].parent;///这个的父亲结点的父亲结点的值
        }
        leaf_information[i].len=n-start; ///二进制码的长度包含末尾
        leaf_code[i]=(char *)malloc((n-start)*sizeof(char));///查找到底之后就进行申请空间,申请n-start的空间
        strcpy(leaf_code[i],&code_temp[start]);///将二进制字符串拷贝到指针数组leaf_code中,也就是把一个叶子结点的编码储存到leaf_code了
    }
    free(code_temp);///用完之后,就释放code_temp内存
    ///操作完之后,叶子结点的编码也就都储存到leaf_code里面了
}
///所有字符的编码
void All_code(char *ch,huffmanCode leaf_code,huffmanCode all_code,leafcode leaf_information,int n,int m)
{
    int i,j;
    for(i=0;i<m;i++)///字符串长度
    {
        for(j=1;j<=n;j++) ///从leaf_information[j].ch中查找与ch[i]相等的下标j
            if(ch[i]==leaf_information[j].ch)
                break;
        all_code[i]=(char *)malloc((leaf_information[j].len)*sizeof(char));///这个字符找到之后,就申请它相应需要的长度
        strcpy(all_code[i],leaf_code[j]); ///拷贝二进制编码到all_code里面,等下进行输出
    }
    ///操作完之后就把刚才的字符串所有的编码都储存起来,等待输出
}
///解码
void transform_huffman(huffman ht,leafcode leaf_code,huffmanCode all_code,int n,int m)
{
    int i=0,j,p;
    while(i<m)///进行刚才输入的字符串的解码
    {
        p=2*n-1;///从父亲结点向下遍历直到叶子结点
        for(j=0;all_code[i][j]!='\0'; j++)///all_code是存储了刚才字符串的所有编码
        {
            if(all_code[i][j]=='0')///如果编码是0,也就是它的左孩子
                p=ht[p].lchild;
            else///否则就是它的右孩子
                p=ht[p].rchild;
        }///走到叶子结点,也就是这个字符的编码,那么就输出这个叶子结点的字符
        printf("%c",leaf_code[p].ch);
        i++;///进行下一个
    }
}
///清屏
void clear()
{
    printf("请按任意键继续......");
    getch();
    system("cls");
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值