【C】哈夫曼编码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#define HFMLENTH 999
#define MAXCODE 10

// 待输入字符串
char str[HFMLENTH];

// 编码
int code[HFMLENTH];

// 哈夫曼树的叶子数 其他节点数 根节点 最大深度
int oriLenth = 0, addLenth = 0, root, maxLayer;

void GetStr()
{
    getchar();
    for (int i = 0; i < strlen(str); i++)
        str[i] = '\0';
    printf("输入字符串长度\n");
    int lenth;
    scanf("%d", &lenth);
    printf("输入字符串\n");
    getchar();
    for (int i = 0; i < lenth; i++)
        scanf("%c", &str[i]);
}

typedef struct
{
    char everyBit[MAXCODE];
    int lenth;
} Code;

// 节点
typedef struct
{
    int leftChild, rightChild, parent;
    char cr;
    int count;
    Code code;
} Node;

// 哈夫曼树
Node * HFMTree;

// 字符串转哈夫曼编码
void StrToHFM()
{
    HFMTree = (Node *)malloc(2 * HFMLENTH * sizeof(Node));
    // 构造节点
    for (int i = 0; i < 2 * HFMLENTH; i++)
    {
        HFMTree[i].cr = '\0';
        HFMTree[i].count = 0;
        HFMTree[i].parent = -1;
        HFMTree[i].leftChild = -1;
        HFMTree[i].rightChild = -1;
    }
    for (int i = 0; str[i] != '\0'; i++)
        for (int j = 0; j < strlen(str); j++)
            if (HFMTree[j].cr == '\0')
            {
                HFMTree[j].cr = str[i];
                HFMTree[j].count = 1;
                if (oriLenth < j)
                    oriLenth = j;
                break;
            }
            else if (HFMTree[j].cr == str[i])
            {
                HFMTree[j].count++;
                break;
            }

    // 构造树
    int finish = 0;
    while (!finish)
    {
        int index1 = 0, index2 = 0;
        for (int i = 0; i <= oriLenth + addLenth; i++)
        {
            if (HFMTree[i].parent == -1 && (HFMTree[index1].parent != -1 || HFMTree[index1].count > HFMTree[i].count))
                index1 = i;
        }
        for (int i = 0; i <= oriLenth + addLenth; i++)
        {
            if (i == index1)
                continue;
            if (index1 == index2 || HFMTree[i].parent == -1 && (HFMTree[index2].parent != -1 || HFMTree[index2].count > HFMTree[i].count))
                index2 = i;
        }
        if (index1 != index2)
        {
            if (HFMTree[index2].parent == -1)
            {
                root = oriLenth + (++addLenth);
                HFMTree[root].count = HFMTree[index1].count + HFMTree[index2].count;
                HFMTree[root].leftChild = index1;
                HFMTree[root].rightChild = index2;
                HFMTree[root].cr = '\0';
                HFMTree[root].parent = -1;
                HFMTree[index1].parent = root;
                HFMTree[index2].parent = root;
            }
            else
            {
                root = index1;
                finish = 1;
            }
        }
        else
        {
            root = index1;
            finish = 1;
        }
    }
}

// 设置哈夫曼编码并打印哈夫曼树
// isPrinting为0时仅仅设置哈夫曼编码 不打印
void PrintHFM(int index, int layer, int isPrinting)
{
    if (index == -1)
        return;

    if (layer > maxLayer)
        maxLayer = layer;

    for (int i = 0; i < layer; i++)
        HFMTree[index].code.everyBit[i] = code[i];
    HFMTree[index].code.lenth = layer;

    if (index == root && HFMTree[index].cr != '\0')
    {
        if (isPrinting == 1)
            printf("%c的编码为:0.\n", HFMTree[index].cr);
        return;
    }

    if (HFMTree[index].cr != '\0')
    {
        if (isPrinting == 1)
        {
            printf("%c的编码为:", HFMTree[index].cr);
            for (int i = 0; i < HFMTree[index].code.lenth; i++)
                printf("%d", HFMTree[index].code.everyBit[i]);
            printf("\n");
        }
    }

    code[layer] = 0;
    PrintHFM(HFMTree[index].leftChild, layer + 1, isPrinting);
    code[layer] = 1;
    PrintHFM(HFMTree[index].rightChild, layer + 1, isPrinting);

}

// 读入文件
void ReadFile()
{
    FILE *fp;
    if ((fp = fopen("test.txt", "rb")) == NULL)
    {
        printf("未找到文件\n");
        return;
    }
    fseek(fp, 0, SEEK_END);
    int fileLen = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    fread(str, fileLen, sizeof(char), fp);
    fclose(fp);
}

void WriteHFMFile()
{
    FILE *fp;
    if ((fp = fopen("hfm.txt", "w")) == NULL)
    {
        printf("无法创建文件\n");
        return;
    }
    for (int i = 0; i < strlen(str); i++)
        for (int j = 0; j <= oriLenth; j++)
            if (str[i] == HFMTree[j].cr)
            {
                for (int k = 0; k < HFMTree[j].code.lenth; k++)
                    fprintf(fp,"%d",HFMTree[j].code.everyBit[k]);
                for (int k = HFMTree[j].code.lenth; k < maxLayer; k++)
                    fprintf(fp, "%d", 0); // 填充
                break;
            }
    fclose(fp);
}

int main()
{
    printf("选择功能:\n1.输入字符串,输出字符串的哈夫曼编码;\n2.输入文本文件(英文),输出哈夫曼编码文件\n");
    char choice;
    scanf("%c", &choice);
    switch(choice)
    {
    case '1':
        GetStr();
        StrToHFM();
        PrintHFM(root, 0, 1);
        break;
    case '2':
        ReadFile();
        StrToHFM();
        PrintHFM(root, 0, 0);
        WriteHFMFile();
        break;
    }
    return 0;
}
View Code

这让我想到高中的时候记笔记,有些字笔画多,就用简单的符号代替了。

实际上哈夫曼文件应该是按位输出的,打开之后可能是一堆乱码。为了方便观察,我就按照字符输出了。

结构体节点Node来组成哈夫曼树,结构体编码Code来表示哈夫曼编码。

用字符数组str读入输入;哈夫曼树的叶子数、其他节点数、根节点、最大深度分别为oriLenth, addLenth, root, maxLayer。

提供用户输入选择功能1.输入字符串,输出字符串的哈夫曼编码;2.输入文本文件(英文),输出哈夫曼编码文件。为此还重温了文件操作代码。

首先使用GetStr()或者ReadFile()获得字符串。

然后用StrToHFM()生成哈夫曼树。

接着用PrintHFM(root, 0, x)来生成编码。当x为1时顺带打印编码。

WriteHFMFile()用来输入哈夫曼压缩文件。

posted @ 2020-05-28 21:49  KamishiroShinchi  阅读(408)  评论(0编辑  收藏  举报