1.任务:

[问题描述]

对一篇不少于5000字符的英文文章(source.txt),统计各字符出现的次数,实现Huffman编码(code.dat),以及对编码结果的解码(recode.txt)。

[基本要求]

(1) 输出每个字符出现的次数和编码,并存储文件(Huffman.txt)。

(2) 在Huffman编码后,英文文章编码结果保存到文件中(code.dat),编码结果必须是二进制形式,即0 1的信息用比特位表示,不能用字符’0’和’1’表示。

(3) 实现解码功能。

2.采用的数据结构

        采用霍夫曼树和顺序查找

3.算法设计思想

        从文件中对于文章进行逐字符读入,对于每一字符出现的总次数进行累加,得到各字母的权值。每一次在未标记的权值中寻找两个最小值,取和,将和加入数组中。将两个最小值进行标记,记录它们的根节点位置(即和所在的位置),记录和的左右子树所在的位置。直至只剩下一个未标记的元素为止,该元素即为霍夫曼树的根节点。根据数组中的位置记录可以得到霍夫曼树。时间复杂度T(n)=O(n^2), 空间复杂度S(n)=O(n).

        利用霍夫曼树进行编码,规定霍夫曼树中左分支为0,右分支为1,从叶结点起,若其为其双亲结点的左节点则记0,为右节点则记1,结点上移,直至移至根节点。将所得的01序列逆序,则得到该叶结点的编码。

        利用霍夫曼树进行解码,遇到0则向左子树下移,遇到1则向右子树下移,直至到达根节点。重复该过程实现01序列的解码。

4.源程序:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;

typedef struct Alpha
{
	char alpha='\0';
	int num=0;
	bool flag=true;
	string code="";
	int Father=0,LChild=0,RChild=0;
}Alpha;
Alpha a[51];

typedef struct BiTNode
{
	Alpha data;
	BiTNode *LChild,*RChild;
}BiTNode,*BiTree;

void ReadFromFile(Alpha a[])
{
	char ch;
	int x;
	FILE *fp;
	if((fp=fopen("source.txt","r"))==NULL)
	{
		cout<<"文件打开失败!\n";
	}
	else
	{
		while(!feof(fp))
		{
			ch=fgetc(fp);
			if(('a'<= ch) && (ch <='z'))
			{
				x=ch -'a';
				a[x].num++;
				continue;
			}
			else if('A'<= ch && ch <='Z')
			{
				x=ch-'A';
				a[x].num++;
				continue;
			}
			else
			{
				continue;
			}
		}
	}
	fclose(fp);
}

int SearchMin(Alpha a[],int n)
{
	int min=5000,k;
	for(int i=0;i<n;i++)
    {
    	if(a[i].flag && (a[i].num<min))
    	{
    		min=a[i].num;
    		k=i;
		}
	}
	return k;
}
char h[20];
void Encode(Alpha a[])
{
    string s;
	for(int i=0;i<26;i++)
	{
		s="";
		int k=0,f=-1,c=i;
		while(f!=0)
		{
			f=a[c].Father;
			if(a[f].LChild==c)
			{
				s+='0';
			}
			if(a[f].RChild==c)
			{
				s+='1';
			}
			c=f;
		}
		reverse(s.begin(),s.end());
		a[i].code=s;
	}
}

void EncodeOutput(Alpha a[])
{
	char ch;
	int x;
	ifstream infile;
	infile.open("source.txt");
	ofstream outfile;
	outfile.open("code.txt");
	if(outfile&&infile)
	{
		while(!infile.eof())
		{
			infile>>noskipws;
			infile>>ch;
	    	if('a'<=ch && ch<='z')
	    	{
	    		x=ch-'a';
	    		outfile<<a[x].code;
	    	}
	    	else if('A'<=ch && ch<='Z')
	    	{
	    		x=ch-'A';
	    		outfile<<a[x].code;
	    	}
	    	else if(ch==' ')
	    	{
	    		outfile<<" ";
			}
	    	else
	    	{
	    		outfile<<ch;
	    	}
		}
	}
	else
	{
		cout<<"打开失败!\n";
	}
	outfile.close();
	infile.close();
}

void RecodeOutput(Alpha a[])
{
	char ch;
	int x;
	ifstream infile;
	infile.open("code.txt");
	ofstream outfile;
	outfile.open("recode.txt");
	if(outfile&&infile)
	{
		infile>>noskipws;
		while(!infile.eof())
		{
			infile>>ch;
	    	if((ch!='0')&&(ch!='1'))
	    	{
	    		outfile<<ch;
			}
			else if(ch==' ')
			{
				cout<<" ";
			}
			else
			{
				int k=50;
				while(k>25)
				{
					if(ch=='0')
					{
						k=a[k].LChild;
					}
					if(ch=='1')
					{
						k=a[k].RChild;
					}
					if(k>25)
					{
						infile>>ch;
					}
				}
				outfile<<a[k].alpha;
			}
		}
	}
	else
	{
		cout<<"打开失败!\n";
	}
	outfile.close();
	infile.close();
}

int main() 
{
	for(int i=0;i<26;i++)
	{
		a[i].alpha='a'+i;
	}
	ReadFromFile(a);
	int n=26;
	for(int i=0;i<25;i++)
	{
		int a1,a2;
		a1=SearchMin(a,n);
		a[a1].flag=false;
		a2=SearchMin(a,n);
		a[a2].flag=false;
		a[n].num=a[a1].num+a[a2].num;
		a[n].flag=true;
		a[n].LChild=a1;
		a[n].RChild=a2;
		a[a1].Father=n;
		a[a2].Father=n;
		n++;
	}
	Encode(a);
	EncodeOutput(a);
	cout<<"文件编码成功!\n";
	RecodeOutput(a);
	cout<<"文件译码成功!\n";
	return 0;
}

5.源程序测试数据及结果

霍夫曼树测试用例

 霍夫曼树编码测试结果

 霍夫曼树解码测试结果

6.存在问题及改进方法

        在霍夫曼树的编码和解码中,为了代码编写的方便起见,我将大小写字母全部转化为了小写字母,这对于编码再解码后的内容可能会造成影响。其次,在编码过程中,只对字母进行了编码,忽视了各种标点符号,我将读入的非字母元素原封不动的输出到了输出文档中,这对于文件的编码的保密程度有了较大的影响,应该增加结点的个数,使各种符号等都加入编码过程才更加严谨。

更多推荐