|
发表于 2019-12-20 20:19:58
|
显示全部楼层
本楼为最佳答案
- //vs的调试功能太好用,我是在vs里写的代码,但其不支持C语言的部分功能,例如变长数组,例如直接使用strlen(即使在C环境下) \
- 所以使用宏定义#ifdef __cplusplus确保程序在传统C环境下也能运行;
- //不使用数组代替二叉树,因为霍夫曼树可能非常“不完全”,其使用数组代替时占用空间可能过大;
- #ifdef __cplusplus
- #define _CRT_SECURE_NO_WARNINGS
- #include <iostream>
- #include <malloc.h>
- #include <memory.h>
- #include <string.h>
- #endif
- #include <stdio.h>
- #define _Out_
- #ifndef NULL
- #define NULL 0
- #endif
- typedef char string[128]; //单个字符编码长度不超过128;
- //霍夫曼书的节点
- typedef struct node_t {
- struct node_t* left;
- struct node_t* right;
- struct node_t* parent;
- int freq;
- char code;
- }node;
- //删除树
- void DestroyTree(node* root) {
- if (!root) return;
- DestroyTree(root->left);
- DestroyTree(root->right);
- free(root);
- };
- //生成编码
- string* generateHuffmanCode(char ch[], int freq[], int N, string _Out_ *codelist) {
- int i, j, k;
- #ifdef __cplusplus
- node *pool[128], *pool_backup[128]; //c++环境下不支持变长数组;
- #else
- node *pool[N], *pool_backup[N];
- #endif
- //为每个字符创建节点
- for (i = 0; i < N; ++i) {
- pool[i] = (node*)malloc(sizeof(node));
- pool[i]->freq = freq[i];
- pool[i]->left = NULL;
- pool[i]->right = NULL;
- pool[i]->parent = NULL;
- }
- //做一个备份
- memcpy(pool_backup, pool, sizeof(pool));
- //每次选取两个节点组合,共需组合N-1次
- for (i = 0; i < N - 1; ++i) {
- int min_freq[2]; //频次最低的两个节点的编号
- memset(min_freq, -1, sizeof(min_freq));
- for (j = 0; j < N - i; ++j) {
- if (min_freq[0] == -1 || pool[j]->freq < pool[min_freq[0]]->freq) {
- min_freq[1] = min_freq[0];
- min_freq[0] = j;
- }
- else if (min_freq[1] == -1 || pool[j]->freq < pool[min_freq[1]]->freq) {
- min_freq[1] = j;
- }
- }
- //组合两个节点,新节点的频次是两个子节点之和
- node* sum = (node*)malloc(sizeof(node));
- sum->left = pool[min_freq[0]];
- sum->right = pool[min_freq[1]];
- sum->parent = NULL;
- sum->left->code = '0';
- sum->right->code = '1';
- sum->left->parent = sum;
- sum->right->parent = sum;
- sum->freq = sum->left->freq + sum->right->freq;
- //于节点池中,用新节点代替原有子节点,并移除另一个子节点
- pool[min_freq[0]] = sum;
- for (j = min_freq[1]; j + 1 < N - i; ++j) {
- pool[j] = pool[j + 1];
- }
- }
- //霍夫曼树生成完毕
-
- pool[0]->code = 0;
- for (i = 0; i < 128; ++i) {
- codelist[i][0] = 0;
- }
- //对于每一个字符对应的节点(即叶节点),向上搜寻直到根节点,路径即为颠倒的编码;
- for (i = 0; i < N; ++i) {
- node* p = pool_backup[i];
- char *line = codelist[ch[i]];
- for (j = 0; p != NULL; ++j, p = p->parent) {
- line[j] = p->code;
- }
- j--;
- //再将颠倒的编码回正
- for (k = 0; k < j / 2; ++k) {
- char t = codelist[ch[i]][k];
- line[k] = line[j - 1 - k];
- line[j - 1 - k] = t;
- }
- }
- //删除霍夫曼树,释放空间
- DestroyTree(pool[0]);
- return codelist;
- }
- //根据编码表编码字符串
- char* encode(char* sz, string codelist[], char* _Out_ szEncoded) {
- char* p = szEncoded;
- while (*sz) {
- int l = strlen(codelist[*sz]);
- strcpy(p, codelist[*sz]);
- p += l;
- sz++;
- }
- return szEncoded;
- }
- //解码字符串
- char* decode(char* szEncoded, string codelist[], char* _Out_ szDecoded) {
- int i, j;
- //根据编码表重新生成树
- node* root = (node*)malloc(sizeof(node));
- root->left = NULL;
- root->right = NULL;
- for (i = 0; i < 128; ++i) {
- node* p;
- char* pch;
- for (p = root, pch = codelist[i]; *pch != 0; ++pch) {
- if (*pch == '0') {
- if (p->left == NULL) {
- p->left = (node*)malloc(sizeof(node));
- p->left->left = NULL;
- p->left->right = NULL;
- }
- p = p->left;
- }
- else if (*pch == '1') {
- if (p->right == NULL) {
- p->right = (node*)malloc(sizeof(node));
- p->right->left = NULL;
- p->right->right = NULL;
- }
- p = p->right;
- }
- }
- p->code = i;
- }
- //根据编码的0或1,进入当前节点的左子树或右子树,直到到达叶节点,并记录
- char* pch, *pret;
- node* p;
- for (p = root, pch = szEncoded, pret = szDecoded; *pch != 0; ++pch) {
- if (*pch == '0') {
- p = p->left;
- }
- else {
- p = p->right;
- }
- if (p->left == NULL) {
- *pret++ = p->code;
- p = root;
- }
- }
- //删除树,释放空间
- DestroyTree(root);
- *pret = '\0';
- return szDecoded;
- }
- int main() {
- string codelist[128];
- char sz[] = { 'A','B','C','D','E','F','G','H','I','J'};
- int freq[] = { 24,20,1,7,9,11,8,15,2,3};
- generateHuffmanCode(sz, freq, sizeof(sz), codelist);
- printf("========编码表================\n");
- unsigned char c;
- for (c = 0; c < 128; ++c) {
- if (codelist[c][0] == 0) continue;
- printf("%c:%s\n", c, codelist[c]);
- }
- char a[] = "ADEC";
- char code[1024];
- char b[10];
- encode(a, codelist, code);
- decode(code, codelist, b);
- printf("===================================\n");
- printf("%s 的编码结果为%s\n", a, code);
- printf("%s 的解码结果为%s\n", code, b);
- #ifdef __cplusplus
- system("pause");
- #endif
- }
复制代码 |
|