【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; }
这让我想到高中的时候记笔记,有些字笔画多,就用简单的符号代替了。
实际上哈夫曼文件应该是按位输出的,打开之后可能是一堆乱码。为了方便观察,我就按照字符输出了。
结构体节点Node来组成哈夫曼树,结构体编码Code来表示哈夫曼编码。
用字符数组str读入输入;哈夫曼树的叶子数、其他节点数、根节点、最大深度分别为oriLenth, addLenth, root, maxLayer。
提供用户输入选择功能1.输入字符串,输出字符串的哈夫曼编码;2.输入文本文件(英文),输出哈夫曼编码文件。为此还重温了文件操作代码。
首先使用GetStr()或者ReadFile()获得字符串。
然后用StrToHFM()生成哈夫曼树。
接着用PrintHFM(root, 0, x)来生成编码。当x为1时顺带打印编码。
WriteHFMFile()用来输入哈夫曼压缩文件。