数据结构课程设计
“哈夫曼编码/译码器
设计一个利用哈夫曼算法的编码和译码系统,重复显示并处理以下项目,知道选择退出为止/
【基本要求】
1)将权值数据存放在数据文件(文件名data.txt 位于执行程序的当前目录中)
2)分别采用动态和静态存储结构
3)初始化:键盘上输入字符集大小n,n个字符和n个权值,建立哈夫曼树
4)编码:利用建好的哈夫曼树生成哈夫曼编码
5)输出编码
6)实现译码功能:即随机输入一组字符,实现编码,并能够根据编号的码翻译成输入的字符。
运行界面如下所示:
具体参考另一篇博客:https://mp.csdn.net/postedit/85055367
源码
/***************************** 哈夫曼编码译码器**********************************************/
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<string.h>
#include<windows.h>
//#include <graphics.h>
//哈夫曼树结点的结构
typedef struct{
char ch;
int weight;
int parent,lchild,rchild;
}HTNode, * HuffmanTree; //动态分配数组存储哈夫曼树
typedef char **HuffmanCode; //动态分配叔叔存储哈夫曼编码表
// 显示菜单
void Menu();
void color(int x);
void gotoxy(int a,int b);
void HuffmanCoding(HuffmanTree &HT, char *charcater,int* w,int n);//建立哈夫曼树
void select(HuffmanTree HT,int j,int *x,int *y); //从建好的树中选两个最小的结点
void Init(); //初始化哈夫曼
void Coding();// 文件编码
void Decoding();// 文件译码
void Print_tree(); //打印哈夫曼树
void printfht(int root,int height);
int Read_tree(HuffmanTree &x);//从文件中读入哈夫曼树
void Decode(HuffmanTree &,char *ToBeTran, int n);
void Encode(HuffmanTree &,HuffmanCode,int);
HuffmanTree HT; //全局变量
int n = 0; //全局变量,存放哈夫曼树叶子结点的数目
int Decodeflag=0;
void first(){ //登陆界面
color(2);
gotoxy(35,8);
printf("哈夫曼编码/译码器3.0");
color(3);
gotoxy(38,14);
printf("制作人:组长 组员 组员");
}
//自定义函根据参数改变颜色
void color(int x)
{
if(x>=0 && x<=15)//参数在0-15的范围颜色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x); //只有一个参数,改变字体颜色
else//默认的颜色白色
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}
/************调用函数设置光标位置 ***************/
void gotoxy(int a,int b){
int xx = 0x0b;
HANDLE hOutput;
COORD loc;
loc.X = a;
loc.Y = b;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出的句柄
SetConsoleCursorPosition(hOutput, loc);//设置光标位置
return;
}
void head()//标题显示
{ color(10);
// gotoxy(20,20);
printf("\t\t\t***********************************************\n");
printf("\t\t\t***********************************************\n");
printf("\t\t\t 欢迎使用哈夫曼编码/译码器\n");
printf("\t\t\t************************************************\n");
printf("\t\t\t************************************************\n");
}
/*********************设置人员滚动字幕****************************/
void gun(){
{
char str[] = "制作人:AAA BBB CCC";//16
int i = 0,j = 0,k = 0,len = 0;
len = strlen(str);
for(i = 0; i < len;i++)
{
k++;
Sleep(200);
printf("%c",str[i]);
if(i == len-1)
{
Sleep(200);
for(j = 0; j < strlen(str); j++)
printf("\b");
for(j=0;j<strlen(str);j++)
{
putchar(' ');
}
i = -1;
}
if( k == 79)
{
for(j = 0; j < strlen(str); j++)
printf("\b");
for(j=0;j<strlen(str);j++)
{
putchar(' ');
}
printf("\r");
k = 0;
i=-1;
}
}
}
}
/********************************操作选择界面*********************************/
void Menu()
{
head();
color(12);
printf("本系统操作功能如下:\n");
printf(" \t\t-----------------------------------------------------\n");
printf(" \t\t-- 请选择操作 --\n");
printf(" \t\t-----------------------------------------------------\n");
printf(" \n");
printf(" \t\t*1---------------初始化赫夫曼树 ---------------\n");
printf(" \t\t*2---------------文件编码 ---------------\n");
printf(" \t\t*3---------------文件译码 ---------------\n");
printf(" \t\t*4---------------打印树 ---------------\n");
printf(" \t\t*0---------------退出 ---------------\n");
printf(" -----------------------------------------------------\n");
printf("请选择你要进行的操作:\n");
}
int main()
{
char select;
first();
getch();
system("cls");
head();
gotoxy(25,25);
printf("请点击任意键继续使用。。。");
getch();
system("cls");
while (1)
{
Menu();
scanf("%c", &select);
switch (select) //选择操作,根据不同的序号选择不同的操作
{
case '1':
system("cls");
Init();//初始化哈夫曼树
break;
case '2':
printf("请往文本中输入要编码的字符串!(编码根据文本XX的哈夫曼树!)\n");
system("cmd /c start 要编码的文件.txt");
Coding();
system("cmd /c start 编码完的文件.txt");
break;
case '3':
printf("请往文本中输入要译码的字码!(译码编码完的文件)\n");
system("cmd /c start 编码完的文件.txt");
Decoding();
system("cmd /c start 译码完的文件.txt");
break;
case '4':
Print_tree();
getch();
system("cls");
break;
case '0':
system("cls");
color(13);
gotoxy(25,25);
printf("欢迎下次使用,再见!!!!!!");
exit(1);
default:
printf("Input error!(输入数字有误,请重新输入))\n");
}
getchar();
}
return 0;
}
//建立一个输入条纹的函数
void printLine()
{
printf("\n---------------------------------------\n");
}
//初始化函数,输入n个字符及其对应的权值,根据权值建立哈夫曼树,并将其存于文件hfmtree中
void Init()
{
FILE *fp;
int i, n, w[256]; //数组存放字符的权值
char character[256]; //存放n个字符
system("cls");
head();
printf("\n输入字符个数:");
scanf("%d", &n); //输入字符集大小
printf("请您按提示输入每种字符以及其对应的权值:\n");
printLine();
getch();
for (i = 0; i<n; i++)
{
char b = getchar();
printf("请您输入第%d个字符:\n",i+1);
scanf("%c", &character[i]);
printf("请输入该字符对应的权值:\n");
scanf("%d", &w[i]); //输入n个字符和对应的权值
printLine();
}
printf("\n\n字符集为:\n");
for(int i=0;i<n;i++)
{
printf("%c:\t%d\n",character[i],w[i]); //将字符和权值打印出来
}
HuffmanCoding(HT, character, w, n); //建立赫夫曼树
printf("下面将输入的字符的哈夫曼编码写入文件hfmtree.txt文件中\n");
if ((fp = fopen("hfmtree.txt", "w")) == NULL)
printf("Open file hfmtree.txt error!(打开hfmtree.txt文件错误)\n");
// fprintf(fp, "%s\t\t%s\t\t%s\t\t%s\t\t%s\t\t%s\n", "单元号", "字符", "权值", "双亲", "左孩子", "右孩子");
for (i = 1; i <= 2 * n - 1; i++)
{
fprintf(fp, "%d %c %d %d %d %d\n", i, HT[i].ch, HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
//将建立的赫夫曼树存入文件hfmtree.txt中
}
printf("\n赫夫曼树建立成功,并已存于文件hfmtree.txt中\n");
fclose(fp);
gotoxy(60,40);
printf("请点击任意键继续...");
getch();
system("cls");
//Menu();
Decodeflag=1;
char *ToBeTran;
HuffmanCode HC;
char ch;
//while(scanf("%c",&ch)!=EOF)
// getch();
color(8);
printf("点击回车查看字符集编码结果:\n");
getch();
// select2=getchar(); //判断是否进行编码/解码测试
while(1)
{
scanf("%c",&ch);
if(ch!='*') //输入*号跳出测试循环
{
Encode(HT,HC,n); //调用编码函数
Decode(HT,ToBeTran,n); //调用解码函数
}
else
break;
//Menu();
}
printf("\n");
printf("\n");
printf("\n");
}
//构造哈夫曼树的算法
void HuffmanCoding(HuffmanTree &HT, char *character, int * w, int n)
{ //w存放n个字符的权值(均>0),构造哈夫曼树HT
int m, i, x, y;
HuffmanTree p;
if (n <= 1) return;
m = 2 * n - 1;
HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));
for (p = HT + 1, i = 1; i <= n; ++i, ++p, ++character, ++w)
{
p->ch = *character;
p->weight = *w;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for (; i <= m; ++i, ++p)
{
p->ch = 0;
p->weight = 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for (i = n + 1; i <= m; ++i)
{
select(HT, i - 1, &x, &y);
HT[x].parent = i;
HT[y].parent = i;
HT[i].lchild = x;
HT[i].rchild = y;
HT[i].weight = HT[x].weight + HT[y].weight;
}
}
//从HT[1]到HT[j]中选择parent为0,weight最小的两个结点,用x和y返回其序号
void select(HuffmanTree HT, int j, int *x, int *y)
{
int i;
//int minwight=65535;
//查找weight最小的结点
for (i = 1; i <= j; i++)
if (HT[i].parent == 0)
{
*x = i; break;
}
for (; i <= j; i++)
if ((HT[i].parent == 0) && (HT[i].weight<HT[*x].weight))
*x = i;
HT[*x].parent = 1;
//查找weight次小的结点
for (i = 1; i <= j; i++)
if (HT[i].parent == 0)
{
*y = i; break;
}
for (; i <= j; i++)
if ((HT[i].parent == 0) && (i != *x) && (HT[i].weight<HT[*y].weight))
*y = i;
/** for(i=1;i<=j;i++)
{
if(HT[i].parent == 0&&HT[i].weight<minweight)
{
*x=i;
minweight=HT[i].weigh;
}
}
HT[*x].parent = 1;
for(i=1;i<=j;i++)
{
if(HT[i].parent == 0&&HT[i].weight<minweight)
{
*y=i;
minweight=HT[i].weigh;
}
}
HT[*y].parent = 1; **/
}
//对文件正文进行编码,然后将结果存入另一个文件中
void Coding()
{
FILE *fp, *fw;
int i, f, c, start,r;
char *cd;
HuffmanCode HC;
n = Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
//求赫夫曼树中各叶子节点的字符对应的的编码,并存于HC指向的空间中
{
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)
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);
}
if ((fp = fopen("要编码的文件.txt", "r")) == NULL)
printf("Open file 要编码的文件.txt error!\n");
if ((fw = fopen("编码完的文件.txt", "w")) == NULL)
printf("Open file 编码完的文件.txt error!\n");
char temp;
while (1)
{
fscanf(fp, "%c", &temp);
printf("%c",temp);
if(temp=='#')
break; //从文件读入下一个字符
i=1;
for (; i <= n; i++)
if (HT[i].ch == temp) //在赫夫曼树中查找字符所在的位置
break;
for (r = 0; HC[i][r] !='\0'; r++) //将字符对应的编码存入文件
{
fputc(HC[i][r], fw);
// putchar(HC[i][r]);
}
}
printf("\n");
for(i=1;i<=n;i++)
printf("%c的编码是: %s\n",HT[i].ch,HC[i]);
printLine();
// gotoxy(30,10);
printf("请点击任意键继续:\n");
fclose(fw);
fclose(fp);
printf("\n已将文件hfmtree.txt成功编码,并已存入编码完的文件.txt中!\n\n");
getch();
system("cls");
// color(5);
}
//从文件hfmtree.txt中读入赫夫曼树,返回叶子节点数 赵东炼
int Read_tree(HuffmanTree &HT)
{
FILE *fp;
int i, n;
HT = (HuffmanTree)malloc(sizeof(HTNode));
if ((fp = fopen("hfmtree.txt", "r")) == NULL)
printf("Open file hfmtree.txt error!\n");
for (i = 1;!feof(fp); i++)
{
HT = (HuffmanTree)realloc(HT, (i + 1) * sizeof(HTNode)); //增加空间
fscanf(fp, "%d %c %d %d %d %d\n",&i,&HT[i].ch,&HT[i].weight,&HT[i].parent,&HT[i].lchild,&HT[i].rchild);
}
fclose(fp);
n = (i + 1) / 2;
return n;
}
//将文件的代码进行译码,结果存入文件textfile中 罗成
void Decoding()
{
FILE *fp, *fw;
int m, i,ct=0,j=0;
char text[300],ch,str[300];
if (n == 0)
n = Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
if ((fp = fopen("编码完的文件.txt", "r")) == NULL)
printf("Open file 编码完的文件 error!\n");
if ((fw = fopen("译码完的文件.txt", "w")) == NULL)
printf("Open file 译码完的文件.txt error!\n");
fscanf(fp, "%s", str);
ch=str[0];
printf("%s",str);
printf("\n");
m = 2 * n - 1;
while(1)
{
if(ch=='0')
m= HT[m].lchild;
else
m=HT[m].rchild;
if (HT[m].lchild ==0&&HT[m].rchild ==0)//从根结点一直找到叶子
{
printf("%c",HT[m].ch);
text[ct++] = HT[m].ch;
m = 2 * n - 1;
}
j++;
ch=str[j];
if(j==strlen(str))
break;
}
if ((HT[m].lchild !=0|| HT[m].rchild != 0) && m != 2 * n - 1)
printf("编码有误!");
text[ct]='\0';
fprintf(fw,"%s",text);
fclose(fp);
fclose(fw);
printf("\n已将编码完的文件成功译码,并且已存入译码完的文件.txt!\n\n");
getch();
system("cls");
}
/*********************对键盘上输入的字符进行解码 ************************/ // 罗成
void Decode(HuffmanTree &hfmTree,char *ToBeTran, int n)
{
int i,ct=0;
char ch;
printLine();
printf("接下来,根据哈夫曼树进行译码的测试:\n");
printf("输入需要二进制译文的编码(以#号结束):\n");
scanf("%c", &ch);
i = 2 * n - 1;//根结点的下标(地址)为2*N-2
while (ch!='#')//#结束后不再翻译
{
if (ch == '0') //‘0’判断左走
i = hfmTree[i].lchild;
else if (ch == '1') //‘1’判断右走
i = hfmTree[i].rchild;
if (hfmTree[i].lchild == 0 || hfmTree[i].rchild == 0) //从根结点一直找到叶子
{
ToBeTran[ct++] = hfmTree[i].ch; //把i中对应的字符赋值给
i = 2 * n - 1; //译完一段编码后置为头结点继续翻译
}
scanf("%c", &ch);
}
if ((hfmTree[i].lchild != 0 || hfmTree[i].rchild != 0) && i != 2 * n- 1)
printf("编码有误!");
ToBeTran[ct] = '\0';
printf("\n");
printf("编码译文为:\n");
printf("%s\n", ToBeTran);
getch();
system("cls");
}
/*************哈夫曼进行编码*******************/ //赵东炼
void Encode(HuffmanTree &,HuffmanCode HC,int n)
{
static int flag=0;
int i, f, c, start,r,j;
char *cd,str[200];
{
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)
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);
}
printf("\n");
if(flag==0)
color(15);
for(i=1;i<=n;i++)
printf("%c的编码是: %s\n",HT[i].ch,HC[i]);
getch();
system("cls");
color(9);
printf("接下来根据哈夫曼树进行编码测试:\n");
gotoxy(30,10);
printf("请点击任意键继续。。。\n");
getch();
system("cls");
flag++;
printf("请输入需要编码的字符串:\n");
scanf("%s",str);
printf("该字符串编码为:\n");
for (i = 0;i<strlen(str); i++)
{
for(j=1;j<=n;j++)
if(str[i]==HT[j].ch)
{
flag=j;
break;
}
printf("%s",HC[flag]);
}
printf("\n");
}
// 打印哈夫曼树
void Print_tree()
{
n = Read_tree(HT);
printfht(2*n-1,n);
}
void printfht(int root,int height)
{
int m,i;
n = Read_tree(HT);
if(root)
{
printfht(HT[root].rchild,height+1);
for(i=0;i<height;i++)
printf("\t");
printf("%d",HT[root].weight);
if(HT[root].lchild==0&&HT[root].rchild==0)
printf("\\\\%c",HT[root].ch);
else
printf("<");
printf("\n");
printfht(HT[root].lchild,height+1);
}
// getch();
// system("cls");
}