ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Huffman编码

2022-01-02 20:33:38  阅读:174  来源: 互联网

标签:编码 结点 weight 哈夫曼 int HT 数组 Huffman


要求:
1、编程实现Huffman编码算法程序;
2、要求程序输出显示所有的码字以及编码效率;
3、设计简单的输入界面(可以是简单的文字提示信息),程序运行时提示用户输入要编码的字符串。

代码:

点击查看代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

//哈夫曼树结点结构
typedef struct
{
    int weight;              //结点权重
    int parent, left, right; //父结点、左孩子、右孩子在数组中的位置下标
} HTNode, *HuffmanTree;
//动态二维数组,存储哈夫曼编码
typedef char **HuffmanCode;

//HT数组中存放的哈夫曼树,end表示HT数组中存放结点的最终位置,s1和s2传递的是HT数组中权重值最小的两个结点在数组中的位置
void Select(HuffmanTree HT, int end, int *s1, int *s2)
{
    int min1, min2;
    //遍历数组初始下标为 1
    int i = 1;
    //找到还没构建树的结点
    while (HT[i].parent != 0 && i <= end)
    {
        i++;
    }
    min1 = HT[i].weight;
    *s1 = i;

    i++;
    while (HT[i].parent != 0 && i <= end)
    {
        i++;
    }
    //对找到的两个结点比较大小,min2为大的,min1为小的
    if (HT[i].weight < min1)
    {
        min2 = min1;
        *s2 = *s1;
        min1 = HT[i].weight;
        *s1 = i;
    }
    else
    {
        min2 = HT[i].weight;
        *s2 = i;
    }
    //两个结点和后续的所有未构建成树的结点做比较
    for (int j = i + 1; j <= end; j++)
    {
        //如果有父结点,直接跳过,进行下一个
        if (HT[j].parent != 0)
        {
            continue;
        }
        //如果比最小的还小,将min2=min1,min1赋值新的结点的下标
        if (HT[j].weight < min1)
        {
            min2 = min1;
            min1 = HT[j].weight;
            *s2 = *s1;
            *s1 = j;
        }
        //如果介于两者之间,min2赋值为新的结点的位置下标
        else if (HT[j].weight >= min1 && HT[j].weight < min2)
        {
            min2 = HT[j].weight;
            *s2 = j;
        }
    }
}

//HT为地址传递的存储哈夫曼树的数组,w为存储结点权重值的数组,n为结点个数
void CreateHuffmanTree(HuffmanTree *HT, int *w, int n)
{
    if (n <= 1)
        return;                                          // 如果只有一个编码就相当于0
    int m = 2 * n - 1;                                   // 哈夫曼树总节点数,n就是叶子结点
    *HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode)); // 0号位置不用
    HuffmanTree p = *HT;
    // 初始化哈夫曼树中的所有结点
    for (int i = 1; i <= n; i++)
    {
        (p + i)->weight = *(w + i - 1);
        (p + i)->parent = 0;
        (p + i)->left = 0;
        (p + i)->right = 0;
    }
    //从树组的下标 n+1 开始初始化哈夫曼树中除叶子结点外的结点
    for (int i = n + 1; i <= m; i++)
    {
        (p + i)->weight = 0;
        (p + i)->parent = 0;
        (p + i)->left = 0;
        (p + i)->right = 0;
    }
    //构建哈夫曼树
    for (int i = n + 1; i <= m; i++)
    {
        int s1, s2;
        Select(*HT, i - 1, &s1, &s2);
        (*HT)[s1].parent = (*HT)[s2].parent = i;
        (*HT)[i].left = s1;
        (*HT)[i].right = s2;
        (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
    }
}

//HT为哈夫曼树,HC为存储结点哈夫曼编码的二维动态数组,n为结点的个数
void HuffmanCoding(HuffmanTree HT, HuffmanCode *HC, int n)
{
    *HC = (HuffmanCode)malloc((n + 1) * sizeof(char *));
    char *cd = (char *)malloc(n * sizeof(char)); //存放结点哈夫曼编码的字符串数组
    cd[n - 1] = '\0';                            //字符串结束符

    for (int i = 1; i <= n; i++)
    {
        //从叶子结点出发,得到的哈夫曼编码是逆序的,需要在字符串数组中逆序存放
        int start = n - 1;
        //当前结点在数组中的位置
        int c = i;
        //当前结点的父结点在数组中的位置
        int j = HT[i].parent;
        // 一直寻找到根结点
        while (j != 0)
        {
            // 如果该结点是父结点的左孩子则对应路径编码为0,否则为右孩子编码为1
            if (HT[j].left == c)
                cd[--start] = '0';
            else
                cd[--start] = '1';
            //以父结点为孩子结点,继续朝树根的方向遍历
            c = j;
            j = HT[j].parent;
        }
        //跳出循环后,cd数组中从下标 start 开始,存放的就是该结点的哈夫曼编码
        (*HC)[i] = (char *)malloc((n - start) * sizeof(char));
        strcpy((*HC)[i], &cd[start]);
    }
    //使用malloc申请的cd动态数组需要手动释放
    free(cd);
}

//打印哈夫曼编码的函数
void PrintHuffmanCode(HuffmanCode htable, int *array, int n, int *str, int s, double h, double l, double *p)
{
    int i, j;
    double e; //编码效率
    //每个字符的霍夫曼编码
    printf("字符霍夫曼编码:\n");
    for (i = 1; i <= n; i++)
    {
        printf("%c:%s\n", array[i - 1], htable[i]);
    }

    //字符串的霍夫曼编码结果
    printf("霍夫曼编码结果:\n");
    for (i = 0; i <= s; i++)
    {
        for (j = 1; j <= n; j++)
        {
            if (str[i] == array[j - 1])
            {
                printf("%s", htable[j]);
            }
        }
    }

    printf("\n");

    //平均码长
    for (i = 1; i <= n; i++)
    {
        l += (p[i - 1] * (double)strlen(htable[i]));
    }

    //编码效率
    e = 100 * h / l; //转为百分数表示
    printf("编码效率:\n%f%\n", e);
}

int main(void)
{
    int i = 0, j = 0, t = 0;
    int s = 0;          //字符串长度
    int n = 0;          //节点个数
    int num[127] = {0}; //声明一个数组用于存放每个字符出现的个数,比字符ASCII码范围稍大即可
    double h = 0;       //信源的熵
    double l = 0;       //平均码长

    printf("请输入字符串长度:\n");
    scanf("%d", &s);

    printf("请输入字符串\n");
    getchar();
    int str[s] = {0}; //字符串数组
    while (i < s)
    {
        str[i] = getchar();
        i++;
    }

    for (i = 0; i < s; i++)
    {
        for (j = 32; j <= 122; j++)
        {
            //ASCII码32-122位,包括数字、字母大小写、空格等符号
            if (str[i] == j)
            {
                num[j]++;
            }
        }
    }

    //统计每个字符的权重
    for (j = 32; j <= 122; j++) //循环统计霍夫曼树结点个数
    {
        if (num[j] > 0)
        {
            n++; //霍夫曼树结点个数
        }
    }

    //权重统计
    int w[n] = {0};     //权重数组
    int array[n] = {0}; //字符数组
    i = 0;
    printf("每个字符的权重:\n");
    for (j = 32; j <= 122; j++) //循环统计霍夫曼树结点个数
    {
        //输出统计信息
        if (num[j] > 0)
        {
            w[i] = num[j];
            array[i] = j;
            printf("%c:%-3d\n", j, w[i]);
            i++;
        }
    }

    //概率计算
    double p[n] = {0}; //字符概率数组
    for (i = 0; i < n; i++)
    {
        p[i] = double(w[i]) / double(s);
        //printf("p[%d]:%f\n", i, p[i]);
    }

    //信源的熵
    for (i = 0; i < n; i++)
    {
        h += (-p[i] * log2(p[i]));
    }

    // int w[5] = {2, 8, 7, 6, 5};
    // int n = 5;
    HuffmanTree htree;
    HuffmanCode htable;
    CreateHuffmanTree(&htree, w, n);
    HuffmanCoding(htree, &htable, n);
    PrintHuffmanCode(htable, array, n, str, s, h, l, p);
    return 0;
}

标签:编码,结点,weight,哈夫曼,int,HT,数组,Huffman
来源: https://www.cnblogs.com/kooop/p/15758225.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有