鱼C论坛

 找回密码
 立即注册
查看: 974|回复: 1

[已解决]该贴无效,请删除

[复制链接]
发表于 2019-12-20 19:21:16 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 小纸张二号 于 2019-12-21 19:32 编辑

设字符集为10个字符,其出现频度如下表所示。先建哈夫曼树,输出对应字符的译码,再利用此树对报文“ADEC”进行编码和译码,输出对应
字符        A        B        C        D        E        F        G        H        I        J
频度        24     20        1        7        9        11        8        15    2        3
最佳答案
2019-12-20 20:19:58
  1. //vs的调试功能太好用,我是在vs里写的代码,但其不支持C语言的部分功能,例如变长数组,例如直接使用strlen(即使在C环境下) \
  2. 所以使用宏定义#ifdef __cplusplus确保程序在传统C环境下也能运行;

  3. //不使用数组代替二叉树,因为霍夫曼树可能非常“不完全”,其使用数组代替时占用空间可能过大;

  4. #ifdef __cplusplus
  5. #define _CRT_SECURE_NO_WARNINGS
  6. #include <iostream>
  7. #include <malloc.h>
  8. #include <memory.h>
  9. #include <string.h>
  10. #endif
  11. #include <stdio.h>

  12. #define _Out_
  13. #ifndef NULL
  14. #define NULL 0
  15. #endif

  16. typedef char string[128];  //单个字符编码长度不超过128;

  17. //霍夫曼书的节点
  18. typedef struct node_t {   
  19.         struct node_t* left;
  20.         struct node_t* right;
  21.         struct node_t* parent;
  22.         int freq;
  23.         char code;
  24. }node;


  25. //删除树
  26. void DestroyTree(node* root) {
  27.         if (!root) return;
  28.         DestroyTree(root->left);
  29.         DestroyTree(root->right);
  30.         free(root);
  31. };


  32. //生成编码
  33. string* generateHuffmanCode(char ch[], int freq[], int N, string _Out_ *codelist) {
  34.         int i, j, k;

  35. #ifdef __cplusplus
  36.         node *pool[128], *pool_backup[128];  //c++环境下不支持变长数组;
  37. #else
  38.         node *pool[N], *pool_backup[N];
  39. #endif

  40.         //为每个字符创建节点
  41.         for (i = 0; i < N; ++i) {
  42.                 pool[i] = (node*)malloc(sizeof(node));
  43.                 pool[i]->freq = freq[i];
  44.                 pool[i]->left = NULL;
  45.                 pool[i]->right = NULL;
  46.                 pool[i]->parent = NULL;
  47.         }
  48.         //做一个备份
  49.         memcpy(pool_backup, pool, sizeof(pool));

  50.         //每次选取两个节点组合,共需组合N-1次
  51.         for (i = 0; i < N - 1; ++i) {
  52.                 int min_freq[2]; //频次最低的两个节点的编号
  53.                 memset(min_freq, -1, sizeof(min_freq));
  54.                 for (j = 0; j < N - i; ++j) {
  55.                         if (min_freq[0] == -1 || pool[j]->freq < pool[min_freq[0]]->freq) {
  56.                                 min_freq[1] = min_freq[0];
  57.                                 min_freq[0] = j;
  58.                         }
  59.                         else if (min_freq[1] == -1 || pool[j]->freq < pool[min_freq[1]]->freq) {
  60.                                 min_freq[1] = j;
  61.                         }
  62.                 }

  63.                 //组合两个节点,新节点的频次是两个子节点之和
  64.                 node* sum = (node*)malloc(sizeof(node));
  65.                 sum->left = pool[min_freq[0]];
  66.                 sum->right = pool[min_freq[1]];
  67.                 sum->parent = NULL;

  68.                 sum->left->code = '0';
  69.                 sum->right->code = '1';
  70.                 sum->left->parent = sum;
  71.                 sum->right->parent = sum;
  72.                 sum->freq = sum->left->freq + sum->right->freq;

  73.                 //于节点池中,用新节点代替原有子节点,并移除另一个子节点
  74.                 pool[min_freq[0]] = sum;
  75.                 for (j = min_freq[1]; j + 1 < N - i; ++j) {
  76.                         pool[j] = pool[j + 1];
  77.                 }
  78.         }
  79.         //霍夫曼树生成完毕

  80.        
  81.         pool[0]->code = 0;
  82.         for (i = 0; i < 128; ++i) {
  83.                 codelist[i][0] = 0;
  84.         }
  85.         //对于每一个字符对应的节点(即叶节点),向上搜寻直到根节点,路径即为颠倒的编码;
  86.         for (i = 0; i < N; ++i) {
  87.                 node* p = pool_backup[i];
  88.                 char *line = codelist[ch[i]];
  89.                 for (j = 0; p != NULL; ++j, p = p->parent) {
  90.                         line[j] = p->code;
  91.                 }
  92.                 j--;

  93.                 //再将颠倒的编码回正
  94.                 for (k = 0; k < j / 2; ++k) {
  95.                         char t = codelist[ch[i]][k];
  96.                         line[k] = line[j - 1 - k];
  97.                         line[j - 1 - k] = t;
  98.                 }
  99.         }

  100.         //删除霍夫曼树,释放空间
  101.         DestroyTree(pool[0]);

  102.         return codelist;
  103. }

  104. //根据编码表编码字符串
  105. char* encode(char* sz, string codelist[], char* _Out_ szEncoded) {
  106.         char* p = szEncoded;
  107.         while (*sz) {
  108.                 int l = strlen(codelist[*sz]);
  109.                 strcpy(p, codelist[*sz]);
  110.                 p += l;
  111.                 sz++;
  112.         }
  113.         return szEncoded;
  114. }

  115. //解码字符串
  116. char* decode(char* szEncoded, string codelist[], char* _Out_ szDecoded) {
  117.         int i, j;

  118.         //根据编码表重新生成树
  119.         node* root = (node*)malloc(sizeof(node));
  120.         root->left = NULL;
  121.         root->right = NULL;
  122.         for (i = 0; i < 128; ++i) {
  123.                 node* p;
  124.                 char* pch;
  125.                 for (p = root, pch = codelist[i]; *pch != 0; ++pch) {
  126.                         if (*pch == '0') {
  127.                                 if (p->left == NULL) {
  128.                                         p->left = (node*)malloc(sizeof(node));
  129.                                         p->left->left = NULL;
  130.                                         p->left->right = NULL;
  131.                                 }
  132.                                 p = p->left;
  133.                         }
  134.                         else if (*pch == '1') {
  135.                                 if (p->right == NULL) {
  136.                                         p->right = (node*)malloc(sizeof(node));
  137.                                         p->right->left = NULL;
  138.                                         p->right->right = NULL;
  139.                                 }
  140.                                 p = p->right;
  141.                         }
  142.                 }
  143.                 p->code = i;
  144.         }


  145.         //根据编码的0或1,进入当前节点的左子树或右子树,直到到达叶节点,并记录
  146.         char* pch, *pret;
  147.         node* p;
  148.         for (p = root, pch = szEncoded, pret = szDecoded; *pch != 0; ++pch) {
  149.                 if (*pch == '0') {
  150.                         p = p->left;
  151.                 }
  152.                 else {
  153.                         p = p->right;
  154.                 }
  155.                 if (p->left == NULL) {
  156.                         *pret++ = p->code;
  157.                         p = root;
  158.                 }
  159.         }

  160.         //删除树,释放空间
  161.         DestroyTree(root);
  162.         *pret = '\0';
  163.         return szDecoded;
  164. }

  165. int main() {
  166.         string codelist[128];
  167.         char sz[] = { 'A','B','C','D','E','F','G','H','I','J'};
  168.         int freq[] = { 24,20,1,7,9,11,8,15,2,3};

  169.         generateHuffmanCode(sz, freq, sizeof(sz), codelist);

  170.         printf("========编码表================\n");
  171.         unsigned char c;
  172.         for (c = 0; c < 128; ++c) {
  173.                 if (codelist[c][0] == 0) continue;
  174.                 printf("%c:%s\n", c, codelist[c]);
  175.         }

  176.         char a[] = "ADEC";
  177.         char code[1024];
  178.         char b[10];
  179.         encode(a, codelist, code);
  180.         decode(code, codelist, b);
  181.         printf("===================================\n");
  182.         printf("%s 的编码结果为%s\n", a, code);
  183.         printf("%s 的解码结果为%s\n", code, b);
  184. #ifdef __cplusplus
  185.         system("pause");
  186. #endif
  187. }
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-12-20 20:19:58 | 显示全部楼层    本楼为最佳答案   
  1. //vs的调试功能太好用,我是在vs里写的代码,但其不支持C语言的部分功能,例如变长数组,例如直接使用strlen(即使在C环境下) \
  2. 所以使用宏定义#ifdef __cplusplus确保程序在传统C环境下也能运行;

  3. //不使用数组代替二叉树,因为霍夫曼树可能非常“不完全”,其使用数组代替时占用空间可能过大;

  4. #ifdef __cplusplus
  5. #define _CRT_SECURE_NO_WARNINGS
  6. #include <iostream>
  7. #include <malloc.h>
  8. #include <memory.h>
  9. #include <string.h>
  10. #endif
  11. #include <stdio.h>

  12. #define _Out_
  13. #ifndef NULL
  14. #define NULL 0
  15. #endif

  16. typedef char string[128];  //单个字符编码长度不超过128;

  17. //霍夫曼书的节点
  18. typedef struct node_t {   
  19.         struct node_t* left;
  20.         struct node_t* right;
  21.         struct node_t* parent;
  22.         int freq;
  23.         char code;
  24. }node;


  25. //删除树
  26. void DestroyTree(node* root) {
  27.         if (!root) return;
  28.         DestroyTree(root->left);
  29.         DestroyTree(root->right);
  30.         free(root);
  31. };


  32. //生成编码
  33. string* generateHuffmanCode(char ch[], int freq[], int N, string _Out_ *codelist) {
  34.         int i, j, k;

  35. #ifdef __cplusplus
  36.         node *pool[128], *pool_backup[128];  //c++环境下不支持变长数组;
  37. #else
  38.         node *pool[N], *pool_backup[N];
  39. #endif

  40.         //为每个字符创建节点
  41.         for (i = 0; i < N; ++i) {
  42.                 pool[i] = (node*)malloc(sizeof(node));
  43.                 pool[i]->freq = freq[i];
  44.                 pool[i]->left = NULL;
  45.                 pool[i]->right = NULL;
  46.                 pool[i]->parent = NULL;
  47.         }
  48.         //做一个备份
  49.         memcpy(pool_backup, pool, sizeof(pool));

  50.         //每次选取两个节点组合,共需组合N-1次
  51.         for (i = 0; i < N - 1; ++i) {
  52.                 int min_freq[2]; //频次最低的两个节点的编号
  53.                 memset(min_freq, -1, sizeof(min_freq));
  54.                 for (j = 0; j < N - i; ++j) {
  55.                         if (min_freq[0] == -1 || pool[j]->freq < pool[min_freq[0]]->freq) {
  56.                                 min_freq[1] = min_freq[0];
  57.                                 min_freq[0] = j;
  58.                         }
  59.                         else if (min_freq[1] == -1 || pool[j]->freq < pool[min_freq[1]]->freq) {
  60.                                 min_freq[1] = j;
  61.                         }
  62.                 }

  63.                 //组合两个节点,新节点的频次是两个子节点之和
  64.                 node* sum = (node*)malloc(sizeof(node));
  65.                 sum->left = pool[min_freq[0]];
  66.                 sum->right = pool[min_freq[1]];
  67.                 sum->parent = NULL;

  68.                 sum->left->code = '0';
  69.                 sum->right->code = '1';
  70.                 sum->left->parent = sum;
  71.                 sum->right->parent = sum;
  72.                 sum->freq = sum->left->freq + sum->right->freq;

  73.                 //于节点池中,用新节点代替原有子节点,并移除另一个子节点
  74.                 pool[min_freq[0]] = sum;
  75.                 for (j = min_freq[1]; j + 1 < N - i; ++j) {
  76.                         pool[j] = pool[j + 1];
  77.                 }
  78.         }
  79.         //霍夫曼树生成完毕

  80.        
  81.         pool[0]->code = 0;
  82.         for (i = 0; i < 128; ++i) {
  83.                 codelist[i][0] = 0;
  84.         }
  85.         //对于每一个字符对应的节点(即叶节点),向上搜寻直到根节点,路径即为颠倒的编码;
  86.         for (i = 0; i < N; ++i) {
  87.                 node* p = pool_backup[i];
  88.                 char *line = codelist[ch[i]];
  89.                 for (j = 0; p != NULL; ++j, p = p->parent) {
  90.                         line[j] = p->code;
  91.                 }
  92.                 j--;

  93.                 //再将颠倒的编码回正
  94.                 for (k = 0; k < j / 2; ++k) {
  95.                         char t = codelist[ch[i]][k];
  96.                         line[k] = line[j - 1 - k];
  97.                         line[j - 1 - k] = t;
  98.                 }
  99.         }

  100.         //删除霍夫曼树,释放空间
  101.         DestroyTree(pool[0]);

  102.         return codelist;
  103. }

  104. //根据编码表编码字符串
  105. char* encode(char* sz, string codelist[], char* _Out_ szEncoded) {
  106.         char* p = szEncoded;
  107.         while (*sz) {
  108.                 int l = strlen(codelist[*sz]);
  109.                 strcpy(p, codelist[*sz]);
  110.                 p += l;
  111.                 sz++;
  112.         }
  113.         return szEncoded;
  114. }

  115. //解码字符串
  116. char* decode(char* szEncoded, string codelist[], char* _Out_ szDecoded) {
  117.         int i, j;

  118.         //根据编码表重新生成树
  119.         node* root = (node*)malloc(sizeof(node));
  120.         root->left = NULL;
  121.         root->right = NULL;
  122.         for (i = 0; i < 128; ++i) {
  123.                 node* p;
  124.                 char* pch;
  125.                 for (p = root, pch = codelist[i]; *pch != 0; ++pch) {
  126.                         if (*pch == '0') {
  127.                                 if (p->left == NULL) {
  128.                                         p->left = (node*)malloc(sizeof(node));
  129.                                         p->left->left = NULL;
  130.                                         p->left->right = NULL;
  131.                                 }
  132.                                 p = p->left;
  133.                         }
  134.                         else if (*pch == '1') {
  135.                                 if (p->right == NULL) {
  136.                                         p->right = (node*)malloc(sizeof(node));
  137.                                         p->right->left = NULL;
  138.                                         p->right->right = NULL;
  139.                                 }
  140.                                 p = p->right;
  141.                         }
  142.                 }
  143.                 p->code = i;
  144.         }


  145.         //根据编码的0或1,进入当前节点的左子树或右子树,直到到达叶节点,并记录
  146.         char* pch, *pret;
  147.         node* p;
  148.         for (p = root, pch = szEncoded, pret = szDecoded; *pch != 0; ++pch) {
  149.                 if (*pch == '0') {
  150.                         p = p->left;
  151.                 }
  152.                 else {
  153.                         p = p->right;
  154.                 }
  155.                 if (p->left == NULL) {
  156.                         *pret++ = p->code;
  157.                         p = root;
  158.                 }
  159.         }

  160.         //删除树,释放空间
  161.         DestroyTree(root);
  162.         *pret = '\0';
  163.         return szDecoded;
  164. }

  165. int main() {
  166.         string codelist[128];
  167.         char sz[] = { 'A','B','C','D','E','F','G','H','I','J'};
  168.         int freq[] = { 24,20,1,7,9,11,8,15,2,3};

  169.         generateHuffmanCode(sz, freq, sizeof(sz), codelist);

  170.         printf("========编码表================\n");
  171.         unsigned char c;
  172.         for (c = 0; c < 128; ++c) {
  173.                 if (codelist[c][0] == 0) continue;
  174.                 printf("%c:%s\n", c, codelist[c]);
  175.         }

  176.         char a[] = "ADEC";
  177.         char code[1024];
  178.         char b[10];
  179.         encode(a, codelist, code);
  180.         decode(code, codelist, b);
  181.         printf("===================================\n");
  182.         printf("%s 的编码结果为%s\n", a, code);
  183.         printf("%s 的解码结果为%s\n", code, b);
  184. #ifdef __cplusplus
  185.         system("pause");
  186. #endif
  187. }
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-12 14:55

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表