Huffman编码与解码 (必做)(Huffman编码、二叉树)
[问题描述]
对一篇不少于5000字符的英文文章(source.txt),统计各字符出现的次数,实现Huffman编码(code.dat),以及对编码结果的解码(recode.txt)。
[基本要求]
(1) 输出每个字符出现的次数和编码,并存储文件(Huffman.txt)。
(2) 在Huffman编码后,英文文章编码结果保存到文件中(code.dat),编码结果必须是二进制形式,即0 1的信息用比特位表示,不能用字符’0’和’1’表示。
(3) 实现解码功能。
数据结构
用到的数据结构为二叉树、文本文件的读写、Huffman的编码与解码。
算法设计与思想
建立一个数组,从文件中一个个读取文章数据,利用哈希表的思想,根据所得字符的ASCII值在数组里分配地址,并对其值加一,以此统计字符的出现次数。
建立一个值为树结点的数组,存储文章的字符,每次选出ASCII值最小的两个字符作为新结点的左右子树,将新节点的权值置为左、右子树上根结点的权,即为ASCII值的和,再将刚刚所选的字符从数组里删除。重复上述步骤,直到构建出一个二叉树为止。设置数组存储编码,利用中序遍历依次递归遍历哈夫曼树,对哈夫曼树中存储的字符调用编码函数进行编码,编码函数也用递归实现,向左走为0,向右为1。对树中结点编码完毕后,从文章中依次读取字符,在树中查找相应的字符并存储编码到文件中;最后再从文件中读取哈弗曼编码,在树中查找字符,0便进入左子树,1便进入右子树,找到后直接输出。
测试数据和结果
源代码
#include<iostream>
#include<stdlib.h>
#include<fstream>
using namespace std;
#define max 257
int zm[max]={0}; //存储每个字母的出现次数
int word[5000]={0}; //存储字符
int num=0; //储存字符的总数量
typedef struct BiTNode
{
int weight; //权值
int word=300; //字母(ASCLL码)
int code[150]; //Huffman编码
BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
void Statistic_zm(int zm[])
{
ifstream readfile;
readfile.open("source.txt");
if(readfile.fail())
{
cout<<"不能打开文件"<<endl;
exit(0);
}
int i=0,j;
char ch;
while(!readfile.eof())
{
readfile.get(ch);
j=(int)ch;
zm[j]++;
word[i]=j;
i++;
}
num=i-2;
/*int sum=0;
for(int k=0;k<max-1;k++)
{
cout<<zm[k]<<" ";
sum=sum+zm[k];
}
cout<<"sum="<<sum;*/
readfile.close();
}
void CreateHuffmanTree(int a[],BiTree &T)
{
BiTree point[max];
BiTNode *b,*q;
int i=0,j,k=0;
for(j=0;j<max;j++)
{
if(a[j]!=0)
{
b=(BiTNode*)malloc(sizeof(BiTNode));
b->word=j;
b->weight=a[j];
b->lchild=NULL; b->rchild=NULL;
point[k]=b;
k++;
}
} //k的数量为结点的数量,point中储存每个结点的地址
/*for(int i=0;i<k;i++)
{
cout<<point[i]->weight<<" ";
}*/
//建立Huffman树
for(i=1;i<k;i++) //k个结点建树,需要k-1次归并
{
int small = -1, big;
//让small初始指向森林中第一棵树,big指向第二棵
for ( j=0; j<k; j++ )
{
if ( point[j]!= NULL && small==-1 )
{
small = j;
continue;
}
if ( point[j]!= NULL )
{
big = j;
break;
}
}
//从当前森林中求出最小权值树和次最小
for ( j=big; j<k; j++)
{
if ( point[j]!= NULL)
{
if ( point[j]->weight < point[small]->weight )
{
big = small ;
small = j;
}
else if ( point[j]->weight < point[big]->weight )
big = j;
}
}
//由最小权值树和次最小权值树建立一棵新树,q指向树根结点
q = (BiTNode *) malloc ( sizeof(BiTNode) );
q->weight = point[small]->weight + point[big]->weight;
q->word = 300;
q->lchild = point[small];
q->rchild = point[big];
point[small] = q;//将指向新树的指针赋给b指针数组中small位置
point[big] = NULL;
}
T = q;
}
void bianma_T ( BiTree T, int b[], int ch, int n )
{
int i;
if ( T )
{
if ( ch == T->word )//复制编码
{
b[0] = n;//存储编码长度
for( i=0; i<=n; i++ )
{
T->code[i] = b[i];
}
}
else
{
b[n]=0;
bianma_T ( T->lchild , b , ch , n+1 );
b[n]=1;
bianma_T ( T->rchild , b , ch , n+1 );
}
}
}
//利用递归依次创建编码
void Creat_HCode ( BiTree T , BiTree B )
{
if ( T == NULL )
{
return;
}
else
{
if ( T->word!=300 )//若此节点为文章内的字符,则对它进行编码
{
int ch = T->word;
int b[100];
bianma_T ( B , b , ch , 1);
}
Creat_HCode ( T->lchild , B );
Creat_HCode ( T->rchild , B);
}
}
void WriteCode ( BiTree T )
{
int i;
if ( T == NULL )
{
return;
}
else
{
WriteCode( T->lchild );
if ( T->word!=300 )//若此节点为文章内的字符,则向Huffman.txt存储结点信息
{
ofstream read_out;
read_out.open( "Huffman.txt", ios::app );
if( !read_out )
{
cout<< "文件打开失败!" <<endl;
return;
}
char ch;
ch = T->word;//将整型转化为字符型
read_out<< "字符:"<< ch <<" 出现次数:"<< T->weight <<" 编码:";
for( i=1; i < T->code[0]; i++ )
read_out<< T->code[i];
read_out<<endl;
read_out.close();
read_out.clear();
}
WriteCode( T->rchild);
}
}
BiTNode* search(BiTree T,int ch)
{
BiTNode* p=NULL;
if(T==NULL) return NULL;
else if(T->word==ch) return T;
else
{
p=search(T->lchild,ch);
if(p==NULL) return search(T->rchild,ch);
else return p;
}
}
void StorageCode(BiTree T)
{ char ch;
BiTNode* p;
ifstream readfile;
readfile.open("source.txt");
fstream file("code.dat",ios::out | ios::binary);
if(readfile.fail())
{
cout<<"不能打开文件"<<endl;
exit(0);
}
int i=0,y,H[20];
while(!readfile.eof())
{
readfile.get(ch);
y=(int)ch;
p=search(T,y);
H[0]=p->code[0];
for(int i=1;i<p->code[0];i++)
H[i]=p->code[i];
file.write((char*)H, sizeof(H));
}
readfile.close();
file.close();
}
int jiema ( BiTree T, int H[] )
{
int i=0;
for( i=1; i<H[0]; i++ )
{
if( H[i]==0 )
T = T->lchild;
else if ( H[i]==1 )
T = T->rchild;
}
return T->word;
}
void CrackCode(BiTree T)
{
int y, H[20];
char ch;
fstream file("code.dat",ios::in | ios::binary);
fstream writefile;
writefile.open("recode.txt");
if(writefile.fail())
{
cout<<"不能打开文件"<<endl;
exit(0);
}
while( !file.eof() )
{
file.read( (char*)H, sizeof(H) );
y = jiema ( T , H );//j存储编码H所对应的字符
ch=(char) y;
writefile.put(ch);
}
}
int main()
{
Statistic_zm(zm);
BiTree T;
CreateHuffmanTree(zm,T);
cout<<endl;
Creat_HCode(T,T);
cout<<endl;
WriteCode( T );
/*char ch;
cin>>ch;
int y=(int)ch;
BiTNode* p=search(T,y);
for(int i=1;i<p->code[0];i++)
printf("%d",p->code[i]);*/
StorageCode(T);
CrackCode(T);
}
Source.txt文件中的内容
Huffman.txt文件中的内容
recode.txt文件中的内容