写一个哈夫曼码的编/译码系统,要求能对要传输的报文进行编码和解码。构造哈夫曼树时,权值小的放左子树,权值大的放右子树,编码时右子树编码为 1,左子树编码为0。
输入表示字符集大小为 n(n <= 100)的正整数,以及 n 个字符和 n 个权值(正整数,值越大表示该字符出现的概率越大);输入串长小于或等于 100 的目标报文。
输出:经过编码后的二进制码,占一行;
以及对应解码后的报文,占一行;
最后输出一个回车符。
这一题要实现哈夫曼树的各种操作,对我而言还是有一定难度的。完整代码如下,关键部分有注释:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
#define M 2 * N - 1
typedef struct
{
char data;
int weight;
int parent;
int lchild;
int rchild;
} HTNode, HuffmanTree[M + 1];//HuffmanTree是一个结构数组类型,0号单元不用
HuffmanTree ht;//哈夫曼树
char hc[M + 1][M];
int n;//n个数据
char s[N];//目标报文
char key[N];//加密报文
void input();
void select(int k, int *min1, int *min2);
void CreateHuffmanTree();
void HuffmanTreeCode();//生成n个字符的哈夫曼编码
void PrintHuffmanTree();//打印哈夫曼编码
void PrintHuffmanTreeDeCoder();//解码
int main()
{
input();
CreateHuffmanTree();
HuffmanTreeCode();
PrintHuffmanTree();
PrintHuffmanTreeDeCoder();
return 0;
}
void input()
{
int i;
ht[0].weight = 10000;//由于后面要寻找最小值和次小值,将ht[0].weight设为无穷大
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
scanf(" %c", &ht[i].data);//读入数据
}
for(i = 1; i <= n; i++)
{
scanf("%d", &ht[i].weight);//读入权值
}
scanf("%s", s);//读入报文
}
void CreateHuffmanTree()//创建哈夫曼树
{
int i;
int m;
int min1, min2;
///1.初始化
for(i = 1; i <= n; i++)
{
ht[i].lchild = ht[i].rchild = ht[i].parent = 0;//1到n号存叶子结点,初始化
}
m = 2 * n - 1;
for(i = n + 1; i <= m; i++)
{
ht[i] = {0, 0, 0, 0, 0};//n + 1到m号存非叶子结点,初始化
}
///2.创建非叶子结点,建立哈夫曼树
for(i = n + 1; i <= m; i++)//n - 1次合并
{
select(i - 1, &min1, &min2);//在ht[1]到ht[i - 1]里找出最小值和次小值,将其编号赋予min1和min2
//合并
ht[i].weight = ht[min1].weight + ht[min2].weight;
ht[i].lchild = min1;
ht[i].rchild = min2;
ht[min1].parent = ht[min2].parent = i;
}
}
void select(int k, int *min1, int *min2)//在ht[1]到ht[k]里找出最小值和次小值,将其编号赋予min1和min2
{
int i;
*min1 = 0;
for(i = 1; i <= k; i++)
{
if(ht[i].parent == 0 && ht[i].weight < ht[*min1].weight)
{
*min1 = i;
}
}
*min2 = 0;
for(i = 1; i <= k; i++)
{
if(ht[i].parent == 0 && ht[i].weight < ht[*min2].weight && i != *min1)
{
*min2 = i;
}
}
}
void HuffmanTreeCode()
{
int i, k;
int start;
int p;
char cd[M];
cd[n - 1] = '\0';
for(i = 1; i <= n; i++)//求n个叶子节点的哈夫曼编码,并将其存储于hc[i]
{
start = n - 1;
k = i;
p = ht[i].parent;
while(p != 0)
{
start--;
if(ht[p].lchild == k)
{
cd[start] = '0';
}
else
{
cd[start] = '1';
}
k = p;
p = ht[k].parent;
}
strcpy(hc[i], &cd[start]);
}
}
void PrintHuffmanTree()
{
int i, j, len;
len = 0;
for(i = 0; s[i] != '\0'; i++)//依次转码打印
{
for(j = 1; ht[j].data != s[i]; j++);//寻找数据
strcpy(key + len, hc[j]);
len = len + strlen(hc[j]);
}
key[len + 1] = '\0';
printf("%s\n", key);
}
void PrintHuffmanTreeDeCoder()//将加密报文key解码
{
int i;
int p;
i = 0;
p = 2 * n - 1;//p为根节点
while(key[i] != '\0')
{
while(ht[p].lchild != 0 || ht[p].rchild != 0)//当p不为叶子节点时,继续读取编码
{
if(key[i] == '0')
{
p = ht[p].lchild;
}
else
{
p = ht[p].rchild;
}
i++;
}
printf("%c", ht[p].data);
p = 2 * n - 1;//p重置为根节点
}
}
以上就是我的实现,其中,为了代码简明,cd并没有使用动态分配,而是直接使用数组。