【数据结构】哈夫曼树的编码与译码
#include <stdio.h>
#include <malloc.h>
#include <string.h>
typedef struct {
char info;
int weight;
int parent, lchild, rchild;
} HTNode, *HuffmanTree;
typedef char* *HuffmanCode;
HuffmanTree HT;
HuffmanCode HC;
int n = 8;
/*
在 HT[1...t] 中选择 parent 不为 0 且权值最小的两个结点,
其序号分别为 s1 和 s2
*/
void Select(HuffmanTree HT, int t, int *s1, int *s2) {
int i, temp1, temp2;
temp1 = temp2 = 1000;
for (i = 1; i <= t; i++) {
if (HT[i].parent == 0 && (HT[i].weight < temp1 || HT[i].weight < temp2)) {
if (temp1 < temp2) {
temp2 = HT[i].weight;
*s1 = i;
} else {
temp1 = HT[i].weight;
*s2 = i;
}
}
}
/* s1 放较小的序号 */
if (*s1 > *s2) {
i = *s1;
*s1 = *s2;
*s2 = i;
}
}
HuffmanTree HuffmanCoding(int *w, int n, char *info) {
// n 为字符串个数
HuffmanTree HT, p;
char *cd;
int m, s1, s2, i, start, f, c;
if (n <= 1) {
return 0;
}
m = 2 * n - 1;
HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode));
p = HT + 1;
for (i = 1; i <= n; ++i, ++p, ++w) {
// 不用第 0 号单元
p->weight = *w;
p->info = info[i-1];
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for (; i <= m; ++i, ++p) {
p->weight = 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for (i = n+1; i <= m; ++i) {
// 在 HT 中选择 parent 为 0 且 weight 最小的两个结点,其序号分别为 s1, s2
Select(HT, i-1, &s1, &s2);
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
/* -------------------- 逆向求哈夫曼编码 -------------------- */
HC = (HuffmanCode)malloc((n+1)*sizeof(char *));
cd = (char *)malloc(n*sizeof(char));
cd[n-1] = '\0';
for (i = 1; i <= n; ++i) {
start = n - 1;
for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent) {
// 从子叶到根逆向2求编码 f 为父结点 c 为子结点
if (HT[f].lchild == c) {
cd[--start] = '0';
} else {
cd[--start] = '1';
}
}
HC[i] = (char *)malloc((n-start)*sizeof(char));
strcpy(HC[i], &cd[start]);
}
free(cd);
return HT;
}
/* 编码 */
void TextToCode() {
char str[50];
int i, j, p;
if (HT == NULL) {
printf("请先进行初始化!\n");
return ;
}
printf("请输入要编码的文本(小写英文字母):\n");
getchar();
gets(str);
printf("编码结果如下:\n");
p = strlen(str);
for (i = 1; i <= p; i++) {
for (j = 1; j <= n; j++) {
if (str[i-1] == HT[j].info) {
printf("%s", HC[j]);
break;
}
}
}
printf("\n");
}
/* 译码 */
void CodeToText() {
int i = 1, j, key;
int i1, i2;
char str[100];
int a = 1;
int m = 2 * n - 1;
if (HT == NULL) {
printf("请先进行初始化!\n");
return ;
}
printf("\n每个字母对应的哈夫曼编码:\n");
/* 以一定格式输出字母对应的编码 */
for (i1 = i; i1 <= 2; i1++) {
printf("┌———————┐ ┌———————┐ ┌———————┐ ┌———————┐\n");
for (i2 = 1; i2 <= 4; i2++) {
printf("| %c:%-11s |", HT[a].info, HC[a]);
a++;
}
printf("\n");
printf("└———————┘ └———————┘ └———————┘ └———————┘\n");
}
printf("\n请输入哈夫曼编码:\n");
scanf("%s", str);
j = strlen(str);
key = m;
printf("哈夫曼编码译码如下:\n");
while (i <= j) {
while (HT[key].lchild != 0) {
if (str[i-1] == '0') {
key = HT[key].lchild;
i++; continue;
}
if (str[i-1] == '1') {
key = HT[key].rchild;
i++; continue;
}
}
printf("%c", HT[key].info);
key = m;
}
printf("\n");
}
void menu() {
printf("┌—————————————————————————┐\n");
printf("| (1) 构造哈夫曼树 | \n");
printf("└—————————————————————————┘\n");
printf("┌—————————————————————————┐\n");
printf("| (2) 哈夫曼编码 | \n");
printf("└—————————————————————————┘\n");
printf("┌—————————————————————————┐\n");
printf("| (3) 哈夫曼译码 | \n");
printf("└—————————————————————————┘\n");
printf("┌—————————————————————————┐\n");
printf("| (0) 退出 | \n");
printf("└—————————————————————————┘\n");
}
int main() {
int i, j = 1;
int wei[8] = {5, 25, 7, 8, 14, 20, 3, 11};
char info[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
for (; j; ) {
menu();
scanf("%d", &i);
switch(i) {
case 1:
HT = HuffmanCoding(wei, n, info);
break;
case 2:
TextToCode();
break;
case 3:
CodeToText();
break;
case 0:
j = 0;
break;
default: printf("输入错误重新输入\n");
}
}
return 0;
}
注意:函数中的数组 可自行更改(其中 中的值对应 中字符的权值)