要传输一则报文内容如下:
“AAAAAAAAAAAAAAABBBBBBBBBCCCCCCCCDDDDDDDDDDDDEEEEEEEEEEFFFFF”
在这段报文中,各个字母出现的次数如下:
请为这段古诗设计哈夫曼编码,要求如下:
- Ø请计算出每个字符出现的概率,并以概率为权重来构造哈夫曼树。
- Ø根据设计的哈夫曼树得到每个字符的哈夫曼编码。
- Ø请将上述设计哈夫曼编码的过程,用代码来实现,并输出各个字母的哈夫曼编码。
- Ø请分析算法的效率,如时间复杂度和空间复杂度等。
- 计算每个字符出现的概率:
A出现的次数为15,概率为15/59=0.254;
B出现的次数为9,概率为10/59=0.153;
C出现的次数为8,概率为8/59=0.136;
D出现的次数为12,概率为12/59=0.203;
E出现的次数为10,概率为10/59=0.169;
F出现的次数为5,概率为5/59=0.085。
2. 构造哈夫曼树:
将每个字符作为一个单独的叶子节点,按照概率从小到大依次构造哈夫曼树,具体过程如下:
1. 首先将概率最小的两个节点合并,构造出新节点,该节点的权值为两个子节点的权值之和,新节点的左右子节点分别为原来的两个节点;
2. 对于新构造出的节点,将其删去,重复步骤1,直到只剩下一个节点,该节点就是哈夫曼树的根节点。
构造好的哈夫曼树如下图所示:- 选择权重最小的两个编码 分别是F:5/59 和 C:8/59 并计算其权重的和为 13/59
- 再次选择权重最小的两个编码 分别是B:9/59 和 E:10/59 并计算其权重的和为 19/59
- 再次选择权重最小的两个编码 分别是D:12/59 和 M1:13/59 并计算其权重的和为 25/59
- 再次选择权重最小的两个编码 分别是A:15/59 和 M2:19/59 并计算其权重的和为 34/59
- 再次选择权重最小的两个编码 分别是M3:25/59 和 M4:34/59 并计算其权重的和为 1 是该哈夫曼树的根节点。
3. 得到每个字符的哈夫曼编码:
对于哈夫曼树中的每个叶子节点,由根节点走到该节点的路径即为该节点对应字符的哈夫曼编码,从左往右为0,从右往左为1。如下表所示:
| 字符 | 概率 | 哈夫曼编码 |
|------|------|-----------|
| A | 0.254| 10 |
| B | 0.153| 110 |
| C | 0.136| 011 |
| D | 0.203| 00 |
| E | 0.169| 111 |
| F | 0.085| 010 |
4.代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 100 // 最大字符数
#define MAXCODE 20 // 最大编码长度
typedef struct // 节点类型
{
char ch; // 字符
double freq; // 字符出现的概率
char code[MAXCODE]; // 字符对应的哈夫曼编码
int parent; // 父节点位置
int left; // 左子树位置
int right; // 右子树位置
} HuffNode, *HuffTree;
void huffman(int n, HuffTree H) // 构造哈夫曼树
{
double min1, min2; // 记录两个最小概率
int x, y; // 两个最小概率对应的节点位置
for (int i = 0; i < 2 * n - 1; i++)
{
H[i].parent = H[i].left = H[i].right = -1;
H[i].code[0] = '\0';
}
for (int i = 0; i < n - 1; i++)
{
x = y = -1;
min1 = min2 = 1.0;
for (int j = 0; j < n + i; j++)
{
if (H[j].parent == -1 && H[j].freq < min1)
{
min2 = min1;
min1 = H[j].freq;
y = x;
x = j;
}
else if (H[j].parent == -1 && H[j].freq < min2)
{
min2 = H[j].freq;
y = j;
}
}
H[x].parent = n + i;
H[y].parent = n + i;
H[n + i].freq = min1 + min2;
H[n + i].left = x;
H[n + i].right = y;
}
}
void getHuffCode(int n, HuffTree H) // 获取哈夫曼编码
{
char code[MAXCODE];
int start, c, p;
code[n - 1] = '\0';
for (int i = 0; i < n; i++)
{
start = n - 1;
c = i;
p = H[i].parent;
while (p != -1)
{
if (H[p].left == c)
{
code[--start] = '0';
}
else
{
code[--start] = '1';
}
c = p;
p = H[p].parent;
}
strcpy(H[i].code, &code[start]);
}
}
int main()
{
char str[MAXN];
scanf("%s", str);
int num[MAXN] = {0};
int n = strlen(str), count = 0;
for (int i = 0; i < n; i++)
{
num[str[i] - 'A']++;
}
for (int i = 0; i < MAXN; i++)
{
if (num[i] > 0)
{
count++;
}
}
HuffTree H = (HuffNode*)malloc(sizeof(HuffNode) * (2 * count - 1));
int j = 0;
for (int i = 0; i < MAXN; i++)
{
if (num[i] > 0)
{
H[j].ch = i + 'A';
H[j].freq = (double)num[i] / n;
j++;
}
}
huffman(count, H);
getHuffCode(count, H);
for (int i = 0; i < count; i++)
{
printf("%c: %s", H[i].ch, H[i].code);
}
free(H);
return 0;
}
运行截图:
5. 算法效率分析:
- 时间复杂度:构造哈夫曼树的时间复杂度为O(nlogn),其中n为叶子节点的个数,获取哈夫曼编码的时间复杂度为O(n),因此总的时间复杂度为O(nlogn)。
- 空间复杂度:构造哈夫曼树的空间复杂度为O(n),其中n为叶子节点的个数,获取哈夫曼编码的空间复杂度为O(nlogn),因此总的空间复杂度为O(nlogn)。