在目录中放一个"in.txt"文件,里边放一篇英语文章,用来统计各个字符出现的频率,从而建树。
哈夫曼树头文件:
#ifndef HUFFMAN_H_
#define HUFFMAN_H_
#include <iostream>
#include <cstring>
#include <iomanip>
using namespace std;
void SelectMin(int weight[],int &amount,int &x,int &y);
void Reverse(char code[],int len);
struct HNode
{
int weight;
int parent;
int lchild;
int rchild;
};
struct HCode
{
char data;
char code[100];
};
class Huffman
{
public:
void CreatHTree(int weight[],int n);//创建哈夫曼树
void CreatCodeTable(char zifu[],int n);//用哈夫曼树建字符表
void displayTable(int n);//显示字符表
void Encode(char *s,char *d,int n);//编码
void Decode(char *s,char *d,int n);//解码
void compress(char *d);//压缩
void uncompress(char outname[],char result[]);//解压缩
~Huffman();
private:
HNode *HTree;
HCode *HCodeTable;
};
//根据数组初始化哈夫曼树
void Huffman::CreatHTree(int weight[],int n)
{
//哈夫曼树的静态三叉链表表示 用-1初始化
HTree = new HNode[2*n-1];
for(int i=0; i<n; i++)
{
HTree[i].weight=weight[i];
HTree[i].lchild=-1;
HTree[i].rchild=-1;
HTree[i].parent=-1;
}
int x=0,y=0;
//x,y 为数组中最小两个元素的索引
int amount=n;
for(int i=n; i<2*n-1; i++)
{
SelectMin(weight,amount,x,y);
HTree[x].parent=HTree[y].parent=i;
HTree[i].weight=HTree[x].weight+HTree[y].weight;
HTree[i].lchild=x;
HTree[i].rchild=y;
HTree[i].parent=-1;
}
}
void Huffman::CreatCodeTable(char zifu[],int n)
{
HCodeTable = new HCode[n];
for(int i=0; i<n; i++)
{
HCodeTable[i].data=zifu[i];
int child =i;
int parent=HTree[i].parent;
int k=0;
while(parent!=-1)
{
if(child==HTree[parent].lchild)
HCodeTable[i].code[k]='0';//左孩子标'0'
else
HCodeTable[i].code[k]='1';//右孩子标'1'0
k++;
child = parent;
parent = HTree[parent].parent;
}
HCodeTable[i].code[k]='\0';
//将编码字符逆置
Reverse(HCodeTable[i].code,k);
}
// cout<<endl<<"Creat HcodeTable successfully!"<<endl;
}
void Huffman::displayTable(int n)
{
cout<<"zifu "<<"code"<<endl;
for(int i=0; i<n; i++)
{
cout<<left<<setw(8)<<HCodeTable[i].data<<HCodeTable[i].code<<endl;
}
}
void Huffman::Encode(char *s,char *d,int n)
{
int i,j;
for(j=0; j<(int)strlen(s); j++)
{
for(i=0; i<n; i++)
{
if(s[j]==HCodeTable[i].data)
{
strcat(d,HCodeTable[i].code);
}
}
}
// cout<<d<<endl;
}
void Huffman::Decode(char *s,char *d,int n)
{
// char d[99999]= {0};
int i=0;
while(*s !=0)
{
int parent=2*n-1-1;//根结点在HTree中的下标
while(HTree[parent].lchild!=-1)
{
if(*s =='0')
parent=HTree[parent].lchild;
else
parent=HTree[parent].rchild;
s++;
}
d[i]=HCodeTable[parent].data;
i++;
}
}
Huffman::~Huffman()
{
//delete [] HCodeTable;
delete [] HTree;
}
//每次选择两个最小的元素,返回其索引,然后求和重新放入数组中
void SelectMin(int weight[],int &amount,int &x,int &y)
{
int i;
int sum=0;
int flag=0;
while(flag<2)
{
//找到数组中最小的元素,并记录其索引
int min=99999;
int index=99999;
for(i=0; i<amount; i++)
{
if(min>weight[i] && weight[i]!=-1)
{
min=weight[i];
index=i;
}
}
sum+=min;
if(flag==0)
{
x=index;
}
if(flag==1)
{
y=index;
}
weight[index]=-1;
flag++;
}
//把和放回到数组中去
weight[amount++]=sum;
}
void Reverse(char code[],int len)
{
char temp[100]= {0};
int j=0;
int i;
for(i=len-1; i>=0; i--,j++)
{
temp[j]=code[i];
}
for(i=0; i<len; i++)
{
code[i]=temp[i];
}
}
void Huffman::compress(char *d)
{
int len=strlen(d);
//若不是8位则补全
while(len%8)
{
d[len]='0';
len++;
}
char storage[10000]= {0};
int i=0;
char value;
for(i=0; i<len; i++)
if(d[i]=='1')
{
value=0b10000000>>(i&0b00000111);
storage[i>>3]|=value;
}
//printf("%x",storage[1]);
cout<<"压缩后存放的路径:"<<endl;
char press_destination[100]= {0};
cin.getline(press_destination,100);
FILE *fout=fopen(press_destination, "w");
{
fwrite(storage, len/8, 1, fout);
}
fclose(fout);
}
void Huffman::uncompress(char outname[],char result[])
{
FILE *uncompress=fopen(outname,"rb");
char storage[9999]= {0};
int n=0;
while(fscanf(uncompress,"%c",&storage[n])==1)
{
n++;
}
//system("pause");
fclose(uncompress);
int i,j,k=0;
char value;
for(i=0; i<n; i++)
{
for(j=0; j<8; j++)
{
value=0b10000000>>(j&0b00000111);
if((storage[i] & value)!=0) result[k]='1';
else result[k]='0';
k++;
}
}
}
#endif
哈夫曼树应用:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <conio.h>
#include "Huffman.h"
using namespace std;
void menu()
{
system("cls");
system("color 5f");
cout<<"Huffman树的建立与编码"<<endl<<endl;
cout<<"1.输出哈夫曼表"<<endl<<endl;
cout<<"2.编码"<<endl<<endl;
cout<<"3.解码"<<endl<<endl;
cout<<"4.压缩"<<endl<<endl;
cout<<"5.解压缩"<<endl<<endl;
//cout<<"h 帮助"<<endl<<endl;
cout<<"e 退出系统"<<endl;
}
struct character
{
char ch;
int num;
} list[256],ctemp;
int main()
{
Huffman hehe;
int weight[300]= {0};
char zifu[256]= {0};
char temp;
int count=0;
int i,j;
//读文件
FILE *fin=fopen("in.txt", "r");
while(1)
{
fscanf(fin,"%c",&temp);
if(feof(fin)) break;
for(i=0; i<count; i++)
{
//表中有该字符,则数量加一
if(temp==list[i].ch)
{
list[i].num++;
break;
}
}
//表中没有该字符,则新增一个结构体来存
if(i==count)
{
list[count].ch=temp;
list[count++].num++;
}
}
fclose(fin);
//排序
for(i=0; i<count-1; i++)
{
for(j=i+1; j<count; j++)
{
if(list[j].num < list[i].num)
{
ctemp=list[j];
list[j]=list[i];
list[i]=ctemp;
}
}
}
for(i=0; i<count; i++)
{
zifu[i]=list[i].ch;
weight[i]=list[i].num;
}
hehe.CreatHTree(weight,count);
hehe.CreatCodeTable(zifu,count);
int choice;
char source[999999]= {0};
char destination[999999]= {0};
while(1)
{
menu();
switch(choice=getch())
{
case '1':
{
system("cls");
hehe.displayTable(count);
system("pause");
break;
}
case '2':
{
system("cls");
memset(source,0,sizeof(char)*strlen(source));
memset(destination,0,sizeof(char)*strlen(destination));
cout<<"请输入要编码的字符:"<<endl;
cin.getline(source,99999);
cout<<"编码结果:"<<endl;
hehe.Encode(source,destination,count);
cout<<destination<<endl;
system("pause");
break;
}
case '3':
{
system("cls");
memset(source,0,sizeof(char)*strlen(source));
memset(destination,0,sizeof(char)*strlen(destination));
cout<<"请输入要解码的字符:"<<endl;
cin>>source;
hehe.Decode(source,destination,count);
cout<<"解码结果: "<<endl;
cout<<destination<<endl;
system("pause");
break;
}
case '4':
{
system("cls");
//初始化清零
memset(source,0,sizeof(char)*strlen(source));
memset(destination,0,sizeof(char)*strlen(destination));
cout<<"请输入要压缩的文本文件的路径:"<<endl;
char press_source[100]= {0};
cin.getline(press_source,100);
FILE *psfin=fopen(press_source,"r");
int n=0;
while(!feof(psfin))
{
fscanf(psfin,"%c",&source[n]);
n++;
}
fclose(psfin);
hehe.Encode(source,destination,count);
hehe.compress(destination);
cout<<"压缩成功!"<<endl;
system("pause");
break;
}
case '5':
{
system("cls");
//初始化清零
memset(source,0,sizeof(char)*strlen(source));
memset(destination,0,sizeof(char)*strlen(destination));
cout<<"请输入要解压缩的文本文件的路径:"<<endl;
char uncompress_source[100]= {0};
cin.getline(uncompress_source,100);
hehe.uncompress(uncompress_source,source);
hehe.Decode(source,destination,count);
// cout<<destination;
cout<<"请输入要存储的文本文件的路径:"<<endl;
char storename[100];
cin.getline(storename,100);
FILE *stofin=fopen(storename, "w");
for(int i=0;i<(int)strlen(destination);i++)
{
fprintf(stofin,"%c",destination[i]);
}
fclose(stofin);
system("pause");
break;
}
/* case 'h':
{
system("cls");
system("pause");
break;
}*/
case 'e':
{
system("cls");
cout<<"感谢使用!"<<endl;
system("pause");
goto end;
break;
}
default:
{
system("cls");
cout<<"没有这个选项,请重新输入!"<<endl;
system("pause");
break;
}
}
}
return 0;
end:
return 0;
}