【问题描述】
给定一组字符的Huffman编码表(从标准输入读取),以及一个用该编码表进行编码的Huffman编码文件(存在当前目录下的in.txt中),编写程序实现对Huffman编码文件的解码,并按照后序遍历序列输出解码过程中Huffman树(规定树中左分支表示0,右分支表示1)中各结点的访问次数。
例如给定的一组字符的Huffman编码表为:
6
1:111
2:0
+:110
*:1010
=:1011
8:100
第一行的6表示要对6个不同的字符进行编码,后面每行中冒号(:)左边的字符为待编码的字符,右边为其Huffman编码,冒号两边无空格。对于该编码表,对应的Huffman树(树中左分支表示0,右分支表示1)应为:
假如给定的Huffman编码文件in.txt中的内容(由0和1字符组成的序列)为:
111011001010011001011111100
则遍历上述Huffman树即可对该文件进行解码,解码后的文件内容为:
12+2*2+2=18
解码过程中,经过Huffman树中各结点的遍边次数见下图中结点中的数字:
对该Huffman树中各结点的访问次数按照后序序列输出应为:
4 1 1 1 2 3 2 2 4 7 11
【输入形式】
先从标准输入读入待编码的字符个数(大于等于2,小于等于50),然后分行输入各字符的Huffman编码(先输入字符,再输入其编码,字符和编码中间以一个英文字符冒号:分隔),编码只由0和1组成。
Huffman编码文件为当前目录下的in.txt文本文件,即:其中的0和1都是以单个字符的形式存储,文件末尾有一个回车换行符。
【输出形式】
先将解码后的文件内容输出到标准输出上(独占一行);然后以后序遍历序列输出解码过程中Huffman树中各结点的访问次数,各数据间以一个空格分隔,最后一个数据后也有一个空格。
【样例输入】
6
1:111
2:0
+:110
*:1010
=:1011
8:100
假如in.txt中的内容为:
111011001010011001011111100
【样例输出】
12+2*2+2=18
4 1 1 1 2 3 2 2 4 7 11
【样例说明】
从标准输入读取了6个字符的Huffman编码,因为规定Huffman树中左分支表示0,右分支表示1,所以利用该编码表可构造上述Huffman树(见图1)。遍历该Huffman树对编码文件in.txt的进行解码,即可得到解码后的原文件内容,遍历过程中各树中结点的最终访问次数要按照后序遍历序列输出。
cpp
#include<stdio.h>
#include<stdlib.h>
typedef struct Node {
char name;
int time;
int found;
struct Node* LeftChild;
struct Node* RightChild;
}Node;
void build(char name, char code[], Node* root) {//根据哈夫曼编码构造哈夫曼树
Node* tree = root;
int i = 0;
for (i = 0; code[i] == '0' || code[i] == '1'; i++) {//通过01编码从根开始寻找字符对应结点,0代表左子结点,1代表右子结点
if (code[i] == '0') {
if (tree->LeftChild == NULL) {//若找不到此节点则新建节点
tree->LeftChild = (Node*)malloc(sizeof(Node));
tree = tree->LeftChild;
tree->time = 0;
tree->found = 0;
tree->LeftChild = NULL;
tree->RightChild = NULL;
}
else tree = tree->LeftChild;
}
if (code[i] == '1') {
if (tree->RightChild == NULL) {
tree->RightChild = (Node*)malloc(sizeof(Node));
tree = tree->RightChild;
tree->time = 0;
tree->found = 0;
tree->LeftChild = NULL;
tree->RightChild = NULL;
}
else tree = tree->RightChild;
}
}
tree->name = name;
}
Node* find(Node* tree) {
if (tree->LeftChild != NULL && tree->LeftChild->found != 1) find(tree->LeftChild);
if (tree->RightChild != NULL && tree->RightChild->found != 1) find(tree->RightChild);//后序遍历
printf("%d ", tree->time);
tree->found = 1;
return tree;
}
int main() {
Node* root = (Node*)malloc(sizeof(Node));
int n;
root->LeftChild = NULL;
root->RightChild = NULL;
root->found = 0;
root->time = 0;
scanf("%d", &n);
getchar();
char name[100];
char code[100][50];
for (int i = 0; i < n; i++) {
scanf("%c:%s", &name[i], code[i]);
getchar();
build(name[i], code[i], root);
}//输入编码表
FILE* fp = fopen("in.txt", "r+");
char a[1000];
int num = 0;
for (; fscanf(fp, "%c", &a[num]) != EOF; num++);//读入代码
Node* tree = root;
for (int i = 0; i < num; i++) {//根据读入的0,1编码从根节点开始搜寻,找到叶节点时输出叶节点对应字符
if (a[i] == '0') {
if (tree->LeftChild == NULL) {
printf("%c", tree->name);
tree->time++;//遍历经过某一节点是,++其被遍历次数
tree = root->LeftChild;
root->time++;
}
else {
tree->time++;
tree = tree->LeftChild;
}
}
else if (a[i] == '1') {
if (tree->RightChild == NULL) {
printf("%c", tree->name);
tree->time++;
tree = root->RightChild;
root->time++;
}
else {
tree->time++;
tree = tree->RightChild;
}
}
}
tree->time++;
printf("%c", tree->name);
printf("\n");
for (; find(root) != root;);//后序遍历
}