哈夫曼编/译码器
- 任务:建立最优二叉树函数。
- 要求:可以建立函数输入二叉树,并输出其哈夫曼树。
在上交资料中请写明:存储结构、基本算法(可以使用程序流程图)、输入输出、源程序、测试数据和结果、算法的时间复杂度、另外可以提出算法的改进方法;
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。
- 一个完整的系统应具有以下功能:
(1)I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。
(2)E:编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。
(3)D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件TextFile中。
(4)P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。
(5)T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint中。
- 题目不分析了,说一下我的代码
0. 假定你看这篇文章的时候已经掌握哈夫曼树原理
1. 原文两种输入方式:手动输入和随机输入
2. 创建哈夫曼树两种方式:通过输入的字符创建 或者 从文件读取
3. Dict数据结构:以键值对的形式存储编码字符和其对应的权值
4. 哈夫曼树以结构体数组形式存储
5. 哈夫曼编码表以二维字符数组形式存储
6. 凹凸打印哈夫曼树使用递归方法,参考
7. plaintext.txt :原文 ciphertext.txt :密文 DecodingResults.txt :解码结果
注意:哈夫曼数组的零号地址不使用
Code,环境codeblocks17 通过
//@ChenYe 2019/2/1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#include <windows.h>
#define MAXSIZE 10000
#define green SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_GREEN|FOREGROUND_INTENSITY)
#define white SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_GREEN)
#define red SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED|FOREGROUND_INTENSITY)
#define yellow SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY)
// 类似green; red; white;的语句为颜色控制语句
const int INF = 0x3f3f3f3f;
const int NINF = 0xc0c0c0c0;
//1061109567 -1061109567 最大最小值
typedef struct
{
char a;
int weight;
} Dict; // Dict以键值对得形式存储编码字符和对应的权值
typedef struct
{
int weight;
int parent,lchild,rchild;
char data;
} HTNode,*HuffmanTree;
typedef char **HuffmanCode;
void RandomGenerate(char *a, int n)
{
// 随机生成字符串
char all[]="ABCDEFGHIJKLMNOPQRSTUVDictXYZabcdefghijklmnopqrstuvwxyz0123456789";
srand(time(0));
for (int i=0; i<n; i++)
{
a[i]=all[rand()%62];
}
a[n]='\0';
green;
printf("The plain is: %s\n",a);
white;
FILE *fp;
fp=fopen("plaintext.txt","w");
fprintf(fp,"%s",a);
fclose(fp);
printf("the plain already save in plaintext.txt\n");
}
void ManuallyInput(char a[],int n)
{
// 手动输入n个字符
printf("plase input the plain:\n");
for(int i=0; i<n; i++) // 输入控制字符数
{
scanf("%c",&a[i]);
}
fflush(stdin);
a[n] = '\0';
green;
printf("The plain is: %s\n",a); // 回显
white;
FILE *fp;
fp=fopen("plaintext.txt","w"); // 保存到文件
fprintf(fp,"%s",a);
fclose(fp);
printf("the plain already save in plaintext.txt\n");
}
void Weight(char *a,Dict w[],int &n)
{ // a 是待加密文本
// Dict 以键值对得形式存储编码字符和对应的权值
// 求得权值后n改为Huffman树中编码字符的个数(即没有重复出现的字符)
for(int j=0; j<=n; j++) // 初始化
w[j].weight=0;
char *p;
p=a;
int i=0,k;
w[i].a=*p;
w[i].weight++;
p++;
while(*p!='\0')
{// 每次循环都遍历w,对当前p所指的字符进行处理
// 若果在w中就权值加一,否则加入加入w
for(k=0; k<=i; k++)
{
if(w[k].a==*p) // 在w中出现
{
w[k].weight++;
break;
}
}
if(k>i) // 不在w中
{
i++;
w[i].a=*p;
w[i].weight++;
}
p++;
}
n=i+1;
for(int x=n; x>0; x--) // w[0]空出来
{
w[x] = w[x-1];
}
printf("编码字符及其权值:\n");
for(i=1; i<=n; i++) // 打印权值
printf("%c",w[i].a);
printf("\n");
for(i=1; i<=n; i++)
printf("%d",w[i].weight);
printf("\n");
}
void Select(HuffmanTree HT,int n,int &s1,int &s2)
{
//在HuffmanTree中筛选权值最小的两个节点下标赋值给s1、s2
int i=1;
int min1=INF,min2=INF;//INF = 1061109567
s1=s2=0;
while(i<=n) // min1<min2<others
{
if(HT[i].parent==0)
{
if(min1>HT[i].weight)
{
min2=min1;
s2=s1;
min1=HT[i].weight;
s1=i;
}
else if(min2>HT[i].weight)
{
s2=i;
min2=HT[i].weight;
}
}// <end if>
i++;
}// <end while>
}
void CreateHuffman(HuffmanTree &HT,Dict w[],int n)
{
// 创建哈弗曼树并存储在HuffmanTree文件中,n为长度
int i,s1,s2;
int m=2*n-1;
if(n<=1)
return ;
HT = (HuffmanTree)malloc((m+1)*sizeof(HTNode));
for(i=0; i<=n; i++)
{
HT[i].weight=w[i].weight;
HT[i].data=w[i].a;
HT[i].lchild=0;
HT[i].parent=0;
HT[i].rchild=0;
}
for(; i<=m; ++i)
{
HT[i].weight=0;
HT[i].lchild=0;
HT[i].parent=0;
HT[i].rchild=0;
}
for(i=n+1; i<=m; i++) //建立哈夫曼树
{
s1=s2=0;
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;
}
// 写入文件
FILE *fp = fopen("HuffmanTree","wb");
fwrite((HuffmanTree)&HT, sizeof(HTNode), m+1, fp);
fclose(fp);
}
void Huffman_CodeTable(HuffmanTree HT,HuffmanCode &HC,int n)
{//得到并打印哈夫曼树的编码表
//求出n个字符的哈夫曼编码HC
if(n<=1)
return ;
printf("编码表:\n");
int i;
HC = (HuffmanCode)malloc((n+1)*sizeof(char*));
char *cd=(char*)malloc(n*sizeof(char));
cd[n-1]='\0';
for(i=1; i<=n; ++i)//倒序求编码字符的哈夫曼编码
{
int start=n-1;
for(int c=i,f=HT[i].parent; f!=0; c=f,f=HT[f].parent)
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);
}
for(i=1; i<=n; i++)
{
printf("%c %d的编码为: %s\n",HT[i].data,HT[i].weight,HC[i]);
}
free(cd);
}
void HuffmanCoding(HuffmanTree HT,HuffmanCode HC,int n,char a[])
{
FILE *fp;
fp=fopen("ciphertext.txt","w"); // 向文件中写入编码后的字符串
green;
printf("\nThe cipher code is:\n");
int len = strlen(a);
for(int i=0; i<len; i++)
{
for(int j=1; j<=n; j++)
{
if(a[i]==HT[j].data)
{
fprintf(fp,"%s",HC[j]);
printf("%s",HC[j]);
}
}
}
white;
printf("\nthe cipher code already save in ciphertext.txt\n");
fclose(fp);
}
void HuffmanDecode(HuffmanTree HT,int n)
{
// Decode函数 从ciphertext.txt读取编码字符串
char decode[50*MAXSIZE]; // 最大加密字符个数MAXSIZE deocde要接收已加密的01字符串,所以空间要大,否则容易溢出
int i,len,p;
FILE *fp,*fp1;
fp=fopen("ciphertext.txt","r");
fscanf(fp,"%s",decode);
fclose(fp);
len=strlen(decode);
p=2*n-1;
green;
printf("Decode Results is:\n");
fp1=fopen("DecodingResults.txt","w");
for(i=0; i<=len; i++) // 解码过程
{
if(HT[p].lchild==0&&HT[p].rchild==0)
{
printf("%c",HT[p].data); // 向屏幕输出
fprintf(fp1,"%c",HT[p].data); // 向文件输出(保存)
p=2*n-1;
}
if(decode[i]=='0')
p=HT[p].lchild;
else if(decode[i]=='1')
p=HT[p].rchild;
}
fclose(fp1);
white;
}
void ReadHuffman_FromFile(HuffmanTree &HT, int &n)
{
// 读取HuffmanTree文件中的哈夫曼树,n被修改为其中编码的字符个数
printf("输入HuffmanTree文件中编码字符的个数:");
scanf("%d",&n);
fflush(stdin);
FILE *fp = fopen("HuffmanTree","rb");
fread((HuffmanTree)&HT, sizeof(HTNode), 2*n, fp); // 因为huffman树是以二进制的形式存入文件的,
//所以读取的时候需要指定读取步长和总长度
fclose(fp);
}
int DEPTH=0;//递归层次
void PreOrderTraverse(HuffmanTree HT,int k)
{
//中序遍历并打印树,结果存放于fp中,k为根结点的下标
int i;
if(HT[k].rchild!=0)
{
DEPTH++;
PreOrderTraverse(HT,HT[k].rchild);
DEPTH--;
}
for(i=0; i<=DEPTH; i++)
printf(" ");
printf("%d\n",HT[k].weight);
if(HT[k].lchild!=0)
{
DEPTH++;
PreOrderTraverse(HT,HT[k].lchild);
DEPTH--;
}
if(HT[k].lchild==0&&HT[k].rchild==0)
return;
return;
} //PreOrderTraverse
void TreePrinting(HuffmanTree HT,int n)
{
//打印树HT,n为树的叶子结点数
// 可以修改PreOrderTraverse函数,打印的凹凸哈夫曼树输出到文件
printf("凹凸哈夫曼树:\n");
PreOrderTraverse(HT,2*n-1);
return;
}//TreePrinting
void start(int choice)
{
int n;
char ch;
char a[MAXSIZE]; // 输入字符串
Dict w[MAXSIZE]; // 键值对形式存储字符串:字符--权值
HuffmanTree Ht; // 哈夫曼树数组
HuffmanCode Hc; // 哈夫曼编码,二维指针
DEPTH = 0;
if(choice == 0)
{
printf("输入待加密字符个数:");
scanf("%d",&n);fflush(stdin);
RandomGenerate(a,n);
Weight(a,w,n); // 求得权值后n改为Huffman树中编码字符的个数(即没有重复出现的字符)
// 创建哈夫曼树
CreateHuffman(Ht,w,n);
// 打印编码表
Huffman_CodeTable(Ht,Hc,n);
// 打印凹凸哈夫曼
TreePrinting(Ht,n);
// 编码
HuffmanCoding(Ht,Hc,n,a);
// 解码
printf("是否要继续进行解码操作?(y/n)\n");
scanf("%c",&ch);fflush(stdin);
if(ch=='y'||ch=='Y')
;
else
return;
HuffmanDecode(Ht,n);
printf("\n");
system("pause");
system("cls");
}
else if(choice == 1)
{
printf("输入字符个数:");
scanf("%d",&n);
fflush(stdin);
ManuallyInput(a,n);
Weight(a,w,n);
// 创建
CreateHuffman(Ht,w,n);
// 打印编码表
Huffman_CodeTable(Ht,Hc,n);
// 打印凹凸哈夫曼
TreePrinting(Ht,n);
// 编码
HuffmanCoding(Ht,Hc,n,a);
// 解码
printf("是否要继续进行解码操作?(y/n)\n");
scanf("%c",&ch);fflush(stdin);
if(ch=='y'||ch=='Y')
;
else
return;
HuffmanDecode(Ht,n);
printf("\n");
system("pause");
system("cls");
}
else if(choice == 2)
{
ReadHuffman_FromFile(Ht,n); // n出函数后就被修改huffman树编码的字符个数
yellow; printf("read file down!\n"); white;
//打印编码表和凹凸哈夫曼树
Huffman_CodeTable(Ht,Hc,n);
TreePrinting(Ht,n);
// 加密
printf("是否要继续进行加密操作?(y/n)\n");
scanf("%c",&ch);fflush(stdin);
if(ch=='y'||ch=='Y')
;
else
return;
printf("输入要加密的字符个数:");
int n2; // n2 表示要输入的原文字符的个数
scanf("%d",&n2);fflush(stdin);
ManuallyInput(a,n2);
HuffmanCoding(Ht,Hc,n,a);
// 解码
printf("是否要继续进行解码操作?(y/n)\n");
scanf("%c",&ch);fflush(stdin);
if(ch=='y'||ch=='Y')
;
else
return;
HuffmanDecode(Ht,n);
printf("\n");
system("pause");
system("cls");
}
else
{
red;
printf("error input!\n");
white;
system("pause");
system("cls");
return;
}
}
int main()
{
int choice=0;
while(1)
{
system("cls");
green;
printf("##::::'##:'##::::'##:'########:'########:'##::::'##:'##::::'##::::'###::::'##::: ##:\n##:::: ##: ##:::: ##: ##.....:: ##.....:: ##:::: ##: ###::'###:::'## ##::: ###:: ##:\n##:::: ##: ##:::: ##: ##::::::: ##::::::: ##:::: ##: ####'####::'##:. ##:: ####: ##:\n#########: ##:::: ##: ######::: ######::: ##:::: ##: ## ### ##:'##:::. ##: ## ## ##:\n##.... ##: ##:::: ##: ##...:::: ##...:::: ##:::: ##: ##. #: ##: #########: ##. ####:\n##:::: ##: ##:::: ##: ##::::::: ##::::::: ##:::: ##: ##:.:: ##: ##.... ##: ##:. ###:\n##:::: ##:. #######:: ##::::::: ##:::::::. #######:: ##:::: ##: ##:::: ##: ##::. ##:\n..:::::..:::.......:::..::::::::..:::::::::.......:::..:::::..::..:::::..::..::::..::");
white;
printf("\nplease input your choice \n0:Randomly generated\n1:Manually Enter\n2:Read FuffmanTree\ntips:强烈建议先通过0/1得到huffman文件再进行读取哈夫曼树的操作\n");
scanf("%d",&choice);
system("cls");
start(choice);
}
}
/*
test data
根据程序提示
*/