鱼C论坛

 找回密码
 立即注册
查看: 2203|回复: 2

[C\C++] 哈夫曼树

[复制链接]
发表于 2024-6-26 09:59:19 | 显示全部楼层 |阅读模式
60鱼币
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. #define N 30
  5. #define MAX 2*N-1

  6. typedef struct HMtree
  7. {
  8.         double weight;
  9.         char s;
  10.         int parent, rchild, lchild;
  11. }HMtree,HM[MAX+1];

  12. void select(HM ht, int x, int* m1, int* m2)//找出森林中全职最小的两个
  13. {
  14.         double min1 = 999;
  15.         double min2 = 999;
  16.         int j;
  17.         for (j = 1; j <= x; j++)
  18.         {
  19.                 if (ht[j].weight < min1 && ht[j].parent == 0)
  20.                 {
  21.                         min1 = ht[j].weight;
  22.                         *m1 = j;
  23.                 }
  24.         }
  25.         int k;
  26.         for (k = 1; k <= x; k++)
  27.         {
  28.                 if (ht[k].weight < min2 && k != *m1 && ht[k].parent == 0)
  29.                 {
  30.                         min2 = ht[k].weight;
  31.                         *m2 = k;
  32.                 }
  33.         }
  34. }

  35. //构建哈夫曼树
  36. void CreatHM(HM ht, int n)
  37. {
  38.         int i;
  39.         for(i=n+1;i<=2*n-1;i++)
  40.         {
  41.                 int m1, m2;
  42.                 select(ht, i - 1, &m1, &m2);
  43.                 ht[i].weight = ht[m1].weight + ht[m2].weight;
  44.                 ht[i].lchild = m1;
  45.                 ht[i].rchild = m2;
  46.                 ht[i].parent = 0;
  47.                 ht[m1].parent = i;
  48.                 ht[m2].parent = i;
  49.                
  50.         }
  51. }

  52. //哈夫曼编码
  53. void HMcoding(HM ht, char** hc, int n)
  54. {
  55.         char* cd = (char*)malloc(n * sizeof(char));
  56.         cd[n - 1] = '\0';
  57.         int now = 1;
  58.         int p = 0;
  59.         int start;
  60.         int i = 0;
  61.         while (i < n)
  62.         {
  63.                 start = n - 1;
  64.                 now = i + 1;
  65.                 p = ht[now].parent;
  66.                 while (p != 0)
  67.                 {
  68.                         start--;
  69.                         if (ht[p].lchild == now)
  70.                         {
  71.                                 cd[start] = '0';
  72.                         }
  73.                         else {
  74.                                 cd[start] = '1';
  75.                         }
  76.                         now = p;
  77.                         p = ht[now].parent;
  78.                        
  79.                 }
  80.                 hc[i + 1] = (char*)malloc((n - start) * sizeof(char));
  81.                 strcpy(hc[i + 1], &cd[start]);
  82.                 i++;
  83.         }
  84.        
  85. }

  86. //先序打印
  87. void dayin(HM ht, char** hc, int index)
  88. {
  89.         if (index >= 1)
  90.         {
  91.                 if (ht[index].lchild == 0 && ht[index].rchild)
  92.                 {
  93.                         printf("%c:%s\n", ht[index].s, hc[index]);
  94.                         return;
  95.                        
  96.                 }
  97.                 dayin(ht, hc, ht[index].lchild);
  98.                 dayin(ht, hc, ht[index].rchild);
  99.         }
  100. }

  101. //在生成的树里查找目标
  102. void chaHM(HM ht, int n,char* pwd)
  103. {
  104.         printf("original:");
  105.         int len = strlen(pwd);
  106.         int i = 0;
  107.         int node = 2 * n - 1;
  108.         while (i < len)
  109.         {
  110.                 if (pwd[i] == '0')
  111.                 {
  112.                         node = ht[node].lchild;
  113.                         i++;
  114.                         if (ht[node].lchild == 0 && ht[node].rchild == 0)
  115.                         {
  116.                                 printf("%c", ht[node].s);
  117.                                 node = 2 * n - 1;
  118.                         }
  119.                 }
  120.                 if (pwd[i] == '1')
  121.                 {
  122.                         node = ht[node].rchild;
  123.                         i++;
  124.                         if (ht[node].lchild == 0 && ht[node].rchild == 0)
  125.                         {
  126.                                 printf("%c", ht[node].s);
  127.                                 node = 2 * n - 1;
  128.                         }
  129.                 }
  130.         }
  131. }

  132. int main()
  133. {
  134.         HM ht;
  135.         int n;//字符个数
  136.         scanf("%d", &n);
  137.         getchar();
  138.         char* hc[4 + 1];
  139.         int i;
  140.         for (i = 1; i <= n; i++)
  141.         {
  142.                 scanf("%c%lf", &ht[i].s, &ht[i].weight);
  143.                 getchar();
  144.                 ht[i].lchild = 0;
  145.                 ht[i].rchild = 0;
  146.                 ht[i].parent = 0;
  147.                
  148.         }
  149.         char pwd[99];
  150.         scanf("%s", pwd);
  151.         CreatHM(ht, n);
  152.         HMcoding(ht,hc,n);
  153.         dayin(ht, hc, 2 * n - 1);
  154.         chaHM(ht, n, pwd);
  155. }
复制代码

我的问题是为什么没有对应每个字符的编码的输出,是函数哪里出问题了吗?
屏幕截图 2024-06-25 214948.png

最佳答案

查看完整内容

主要问题出在 dayin 函数的实现中。 在判断叶子节点时存在逻辑错误,导致没有正确打印字符的编码。 修复后的完整代码如下:
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-6-26 09:59:20 | 显示全部楼层
本帖最后由 小甲鱼的二师兄 于 2025-1-17 19:04 编辑

主要问题出在 dayin 函数的实现中。

在判断叶子节点时存在逻辑错误,导致没有正确打印字符的编码。

修复后的完整代码如下:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #define N 30
  5. #define MAX 2 * N - 1

  6. typedef struct HMtree {
  7.     double weight;
  8.     char s;
  9.     int parent, rchild, lchild;
  10. } HMtree, HM[MAX + 1];

  11. void selectMin(HM ht, int x, int* m1, int* m2) {
  12.     double min1 = 999;
  13.     double min2 = 999;
  14.     int j;
  15.     for (j = 1; j <= x; j++) {
  16.         if (ht[j].weight < min1 && ht[j].parent == 0) {
  17.             min1 = ht[j].weight;
  18.             *m1 = j;
  19.         }
  20.     }
  21.     int k;
  22.     for (k = 1; k <= x; k++) {
  23.         if (ht[k].weight < min2 && k != *m1 && ht[k].parent == 0) {
  24.             min2 = ht[k].weight;
  25.             *m2 = k;
  26.         }
  27.     }
  28. }

  29. // 构建哈夫曼树
  30. void CreatHM(HM ht, int n) {
  31.     int i;
  32.     for (i = n + 1; i <= 2 * n - 1; i++) {
  33.         int m1, m2;
  34.         selectMin(ht, i - 1, &m1, &m2);
  35.         ht[i].weight = ht[m1].weight + ht[m2].weight;
  36.         ht[i].lchild = m1;
  37.         ht[i].rchild = m2;
  38.         ht[i].parent = 0;
  39.         ht[m1].parent = i;
  40.         ht[m2].parent = i;
  41.     }
  42. }

  43. // 哈夫曼编码
  44. void HMcoding(HM ht, char** hc, int n) {
  45.     char* cd = (char*)malloc(n * sizeof(char));
  46.     cd[n - 1] = '\0';
  47.     int now = 1;
  48.     int p = 0;
  49.     int start;
  50.     int i = 0;
  51.     while (i < n) {
  52.         start = n - 1;
  53.         now = i + 1;
  54.         p = ht[now].parent;
  55.         while (p != 0) {
  56.             start--;
  57.             if (ht[p].lchild == now) {
  58.                 cd[start] = '0';
  59.             } else {
  60.                 cd[start] = '1';
  61.             }
  62.             now = p;
  63.             p = ht[now].parent;
  64.         }
  65.         hc[i] = (char*)malloc((n - start + 1) * sizeof(char)); // 改为从 0 开始
  66.         strcpy(hc[i], &cd[start]);
  67.         i++;
  68.     }
  69.     free(cd);
  70. }

  71. // 先序打印
  72. void dayin(HM ht, char** hc, int index) {
  73.     if (index >= 1) {
  74.         if (ht[index].lchild == 0 && ht[index].rchild == 0) { // 叶子节点
  75.             printf("%c:%s\n", ht[index].s, hc[index - 1]); // 索引从 0 开始
  76.             return;
  77.         }
  78.         // 遍历左子树
  79.         dayin(ht, hc, ht[index].lchild);
  80.         // 遍历右子树
  81.         dayin(ht, hc, ht[index].rchild);
  82.     }
  83. }

  84. // 在生成的树里查找目标
  85. void chaHM(HM ht, int n, char* pwd) {
  86.     printf("original:");
  87.     int len = strlen(pwd);
  88.     int i = 0;
  89.     int node = 2 * n - 1;
  90.     while (i < len) {
  91.         if (pwd[i] == '0') {
  92.             node = ht[node].lchild;
  93.             i++;
  94.             if (ht[node].lchild == 0 && ht[node].rchild == 0) { // 到达叶子节点
  95.                 printf("%c", ht[node].s);
  96.                 node = 2 * n - 1;
  97.             }
  98.         } else if (pwd[i] == '1') {
  99.             node = ht[node].rchild;
  100.             i++;
  101.             if (ht[node].lchild == 0 && ht[node].rchild == 0) { // 到达叶子节点
  102.                 printf("%c", ht[node].s);
  103.                 node = 2 * n - 1;
  104.             }
  105.         } else { // 非法输入
  106.             printf("\nError: Invalid character in encoded string.\n");
  107.             return;
  108.         }
  109.     }
  110.     printf("\n");
  111. }

  112. int main() {
  113.     HM ht;
  114.     int n; // 字符个数
  115.     printf("Enter the number of characters:\n");
  116.     scanf("%d", &n);
  117.     getchar();
  118.     char* hc[N]; // 动态调整大小,索引从 0 开始
  119.     int i;
  120.     printf("Enter characters and their weights (format: <char> <weight>):\n");
  121.     for (i = 1; i <= n; i++) {
  122.         scanf("%c %lf", &ht[i].s, &ht[i].weight);
  123.         getchar();
  124.         ht[i].lchild = 0;
  125.         ht[i].rchild = 0;
  126.         ht[i].parent = 0;
  127.     }
  128.     char pwd[99];
  129.     printf("Enter the encoded string:\n");
  130.     scanf("%s", pwd);
  131.     CreatHM(ht, n);
  132.     HMcoding(ht, hc, n);
  133.     printf("Huffman Codes:\n");
  134.     dayin(ht, hc, 2 * n - 1);
  135.     chaHM(ht, n, pwd);
  136.     // 释放动态分配的内存
  137.     for (i = 0; i < n; i++) {
  138.         free(hc[i]);
  139.     }
  140.     return 0;
  141. }
复制代码

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2024-8-25 10:33:25 | 显示全部楼层
您的代码中存在几个问题,可能导致没有对应每个字符的编码输出。以下是我发现的一些问题和相应的解决方案:

1. **`HMcoding`函数中的内存分配问题**:
   在`HMcoding`函数中,您为`cd`分配了`n * sizeof(char)`大小的内存,但实际需要的内存应该是`n + 1`,因为字符串的结尾需要一个空字符`'\0'`。

   ```c
   char* cd = (char*)malloc((n + 1) * sizeof(char)); // 修改这里
   ```

2. **`HMcoding`函数中的字符串复制问题**:
   在复制编码到`hc[i + 1]`时,您应该使用`strncpy`来确保正确复制字符串,并且要设置目标字符串的结束符。

   ```c
   strncpy(hc[i + 1], &cd[start], n - start);
   hc[i + 1][n - start] = '\0'; // 确保字符串正确结束
   ```

3. **`HMcoding`函数中的逻辑问题**:
   您的`HMcoding`函数逻辑似乎有误。您应该从叶子节点开始构建编码,而不是从根节点开始。此外,您需要为每个字符分配一个编码,而不是只分配一个。

4. **`dayin`函数的实现问题**:
   您的`dayin`函数似乎是想实现先序遍历打印哈夫曼树,但是实现有误。您需要一个递归函数来正确地打印每个节点的值和对应的编码。

5. **`chaHM`函数中的逻辑问题**:
   在`chaHM`函数中,当找到叶子节点时,您应该继续打印字符,直到遍历完整个密码串。

6. **`main`函数中的`hc`数组初始化问题**:
   您需要为`hc`数组的每个元素分配足够的内存,以存储可能的最长编码。

7. **`main`函数中的输入问题**:
   在读取字符和权重后,您使用了`getchar()`来消耗换行符,这在某些情况下可能会导致问题。确保输入格式正确,并且`getchar()`的使用是恰当的。

8. **编码的打印**:
   您需要在构建完哈夫曼树和编码后,打印出每个字符对应的编码。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-5-4 15:10

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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