哈夫曼编码 C 实现

@[TOC] C语言哈夫曼编码

哈夫曼编码 C 实现

思考

  1. 之前做过一个编码,解码的,都是先有一个密码本。
    1. 编码:输入明文的字符串根据码本编码,
    2. 解码:输入密文的密码,跟局码本转换为明文的字符串。
    3. 所以第一件事应该是构造一个密码本。
  2. 构造码本,码本的存储应该用[哈夫曼树],不然就不叫哈夫曼编码了。哈夫曼树的构造:参考《大话数据结构》的步骤哈夫曼树构造步骤
    树节点的结构体,首先要能存放 字符,要有权值,树要使用孩子表示法,所以需要左右孩子的指针,既然是码本,还要存放字符对应的编码。要定义一个这样的结构体。

代码过程:

  1. 控制台输入一串字符,读取,并统计不同字符出现的次数(作为权重)。以小写英文字母为例子(alpha:a~z ascii 97 ~122)
  2. 用1的字符串构造哈夫曼树,完成密码本
  3. 根据密码本对输入的字符串做编解码

统计控制台输入字符,然后构造一个码本

//哈夫曼树节点
typedef struct huffNode{
    char value;
    int  weight;
    //huffNode *par;
    huffNode *lchild;
    huffNode *rchild;
    char *code;
}huffNode, *huffList;

//读取数据并且统计权值
huffList readStr(int *num)
{   //假设只是输入小写字符
    //读取字符串以#结尾
    char tmp;
    int i = 0;
    int  weight[26];
    huffList List = NULL;
    for(; i < 26; i++ )
    {
        weight[i] = 0;
    }
    printf("输入数据创建码本\n");
    scanf("%c",&tmp);
    while(tmp != '#')
    {
        weight[tmp - 97] += 1;
        scanf("%c",&tmp);
        //(*num)++;
    }
    for(i = 0;i<26;i++)
    {
        if(weight[i] != 0)
            (*num)++;
    }
    List = (huffList )malloc(sizeof(huffNode) * (*num));
    for(i = 0;i < 26;i++)
    {
        if(weight[i] != 0)
        {
            List[i].value = 97 + i;
            List[i].weight = weight[i];
            List[i].lchild = NULL;
            List[i].rchild = NULL;
            List[i].code = "\0";
        }

    }
    return List;
}
//构建霍夫曼树,参考大话数据结构的步骤
huffList creatTree(huffList List,int len)
{
    int i = 0,j = 0,newlen = len;
    huffNode *newTree = NULL;
    sortTree( List, len);           //使用插入排序算法
    for(j = 1; j < len; j++)
    {
        huffNode *lfTree = allocNode();
        huffNode *rtTree = allocNode();
        newTree = allocNode();
        cover(lfTree,&List[j]);
        cover(rtTree,&List[j-1]);
        (*newTree).weight = (*lfTree).weight + (*rtTree).weight;
        (*newTree).lchild = lfTree;
        (*newTree).rchild = rtTree;
        cover(&List[j],newTree);
        newlen--;
        sortTree(&List[j],newlen);
    }
    return newTree;
}
//完成码本  对树总的字符编码:树的左边路径 + 0 ,右边路径 + 1;
void huffCode(huffList List)
{
    //int test = 0;
    if(List->lchild || List->rchild)
    {   
        if(List->lchild)
        {
            List->lchild->code = (char *)malloc(sizeof(char) * strlen(List->code) + 1);
            strcpy(List->lchild->code,List->code);
            strcat(List->lchild->code,"0");
        }
        if(List->rchild)
        {
             List->rchild->code = (char *)malloc(sizeof(char) * strlen(List->code) + 1);
            strcpy(List->rchild->code,List->code);
            strcat(List->rchild->code,"1");
        }
        if(List->lchild)
            huffCode(List->lchild);
        if(List->rchild)
            huffCode(List->rchild);
    }
}

利用构造好的码本进行编解码

void Encode(huffList List)
{
    char tmp;
    char arr[MAX_SIZE];
    codeBook *cb;
    int i = 0,j = 0;
    int strLen = 0;
    int cbLen = 0;
    huffList ptr = List;
    huffQuePtr hptr;
    hptr.front=hptr.rear = NULL;
    codeBook tmpCode;
    char *aftercode = "\0";
    char *chPtr = NULL;
    //用数组存输入数据
    char *str = "\0";
    fflush(stdin);
    printf("输入待编码的字符串\n");
    scanf("%c",&tmp);
    while(tmp != '#' && i < 255)
    {
        arr[strLen] = tmp;
        scanf("%c",&tmp);
        strLen++;
    }
    cb = (codeBook *)malloc(sizeof(codeBook)*strLen);
    //需要二叉树的层序遍历    
    //越靠近上层的节点出现的概率越高理论上这样编码是快的,但是本场景下,如果使用查找数组的方式编码 时间复杂度会是O(1)
    //解码感觉程序遍历 哈夫曼树,每个叶子节点的 code 作为字串用 kmp 算法匹配 代解码字符串这样会快一些。
    // 编码需要层序遍历,参考:https://blog.csdn.net/m0_37925202/article/details/80796010   
    //用数组存码本,index 0 ~ 100 ,权重高->权重低
    queuePush(&hptr,*List);
    while(!isEmpty(hptr))
    {   
        if(hptr.front->node.value != 0)
        {
            tmpCode.val = hptr.front->node.value;
            tmpCode.code=hptr.front->node.code;
            memcpy(&cb[cbLen],&tmpCode,sizeof(codeBook));
            cbLen++;
        }
        if(hptr.front->node.lchild != NULL)
        {
             queuePush(&hptr,*(hptr.front->node.lchild));
        }
        if(hptr.front->node.lchild != NULL)
        {
             queuePush(&hptr,*(hptr.front->node.rchild));
        }
        //已经操作过的可以出列。
        popQ(&hptr);
    }

    //编码
    for(i = 0;i<strLen;i++)
    {
        while(j<cbLen)
        {
            if(arr[i] == cb[j].val)
            {
                chPtr = (char *)malloc(sizeof(char) * (strlen(aftercode) + strlen(cb[j].code)));
                strcpy(chPtr,aftercode);
                aftercode = chPtr;
                strcat(aftercode,cb[j].code);
                break;
            }
            j++;
        }
        if(j==cbLen)
        {
            printf("字符 %c 不在码本中\n",arr[i]);
        }
        j = 0;
    }

    printf("编码之后: %s\n",aftercode);
    //解码
    printf("解码 :");
    while(strlen(aftercode))
    {
        if(*(aftercode)== '0')
        {
            ptr = ptr->lchild;
            if(ptr->value != 0)
            {
                printf("%c",ptr->value);
                ptr = List;
            }
        }
        else
        {
             ptr = ptr->rchild;
            if(ptr->value != 0)
            {
                printf("%c",ptr->value);
                ptr = List;
            }
        }
        aftercode++;
    }
    printf("hello \n");
}

运行效果代码下载:
https://download.csdn.net/download/weixin_42592516/20632220

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值