【算法】贪心算法-哈夫曼压缩算法-无密/加密压缩、解压缩

本文介绍了哈夫曼编码在文件压缩中的应用,通过在Visual C++6.0环境下实现,详细阐述了压缩和解压缩的步骤。压缩过程中包括统计字符频度、构建哈夫曼树和生成哈夫曼编码;解压缩则依赖于元数据重建哈夫曼树,按照编码读取信息。
摘要由CSDN通过智能技术生成

开发环境:

Visual C++6.0

代码:

#include <iostream>
#include <windows.h>
#include <fstream>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
typedef struct
{
	int  weight;
	int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef struct
{
	char* data;
	int* num;
	int length;
}TNode;
typedef struct
{
	char *data;
	char** HM;
}Code;
typedef char** HuffmanCode;
bool IfEncode()
{
	int choose;	
	cout<<"\t            1.加密压缩     2.无密压缩"<<endl;
	cout<<"请输入选择:"; 
	cin>>choose;
	if(choose==1)
		return true;
	else
		return false;
}
void Encode(vector<char>& v)
{
	char ch[30];
	v.push_back('@');
	cout<<"请输入压缩密码"<<endl; 
	cin>>ch;
	for(int i=0;ch[i]!='\0';i++)
		v.push_back(ch[i]);
	v.push_back('\0');
	v.push_back('@');
	cout<<"开始压缩…"<<endl; 
}
void ReadTxt(vector<char>& v)
{
	char ch;
	ifstream infile("vae.txt",ios::in);//读取需要压缩的文件vae.txt
	if(!infile)
	{
		cerr<<"open error"<<endl;
		exit(1);
	}
	if(IfEncode())
		Encode(v);
	while(infile.peek()!=EOF)
	{
		infile.get(ch);
		v.push_back(ch); 
	}	
	infile.close();	
}
void InitList(TNode& T)
{
	T.data=new char[256];
	T.num=new int[256];
	if(!T.data||!T.num)
		exit(1);
	T.length=0;
}
bool Find(TNode T,char ch)
{
	int i;
	for(i=0;i<T.length;i++)
		if(ch==T.data[i])
			return true;
	return false;
}
void TCount(vector<char> v1,TNode &T)
{
	int i,j=0;
	char ch;
	int m=v1.size();
	for(i=0;i<m;i++)
	{
		ch=v1[i];
		if(!Find(T,ch))
		{
			T.data[j]=ch;
			T.num[j]=count(v1.begin(),v1.end(),ch);
			j++;
			T.length++;
		}		
	}		
}
void Select(HuffmanTree &HT,int m,int& s1,int& s2)
{
	int k,j,n,min=32767;
	for(k=1;k<=m;k++)s
			if(HT[k].weight<=min)
			{
				j=k;
				min=HT[k].weight;
			}			
	}
	s1=j;
	HT[j].parent=1;
	min=32767;
	for(k=1;k<=m;k++)
	{
		if(HT[k].parent==0)
			if(HT[k].weight<=min)
			{
				n=k;
				min=HT[k].weight;
			}			
	}
	s2=n;
}
void CreateHuffmanTree (HuffmanTree &HT,TNode T,int length)
{
	int m,i,s1,s2;
	if(length<=1)
		return;
	m=2*length-1;
	HT=new HTNode[m+1];
	for(i=1;i<=m;++i)
	{
		HT[i].parent=0;
		HT[i].lchild=0;
		HT[i].rchild=0;
	}
	for(i=1;i<=length;++i)
		HT[i].weight=T.num[i-1];	
	for(i=length+1;i<=m;i++)
	{
		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;
	}	
}
void CreatHuffmanCode (HuffmanTree HT,HuffmanCode &HC,int n)
{
	int i,f,c,start;
	HC=new char*[n+1];
	char* cd=new char[n];
	cd[n-1]='\0';
	for(i=1;i<=n;i++)
	{
		start=n-1;
		c=i;
		f=HT[i].parent;
		while(f!=0)
		{
			--start;
			if(HT[f].lchild==c)
				cd[start]='0';
			else
				cd[start]='1';
			c=f;
			f=HT[f].parent;
		}
		HC[i]=new char[n-start];
		strcpy(HC[i],&cd[start]);
	}	
	delete cd;
}

void Zip(HuffmanCode HC,vector<char> v,TNode T)
{
	int i=0,j=0,k=0;
	ofstream outfile("com.zip",ios::out);//输出压缩后的文件com.zip
	if(!outfile)
	{
		cerr<<"open error"<<endl;
		exit(1);
	}
	for(i=0;i<v.size();i++)
	{
		for(j=0;j<T.length;j++)
			if(T.data[j]==v[i])
				break;
		for(k=0;HC[j+1][k]!='\0';k++)
			outfile<<HC[j+1][k];			
	}
	outfile.close();
	cout<<"正在压缩…";Sleep(500);cout<<" 。";Sleep(500);cout<<" 。"<<endl;
	cout<<"压缩成功,可到com.zip中查看压缩后文件"<<endl; 
} 
bool REncode(char ch[])
{
	int i=0;
	char code[30];
	cout<<"请输入解压密码:"<<endl;
	cin>>code;
	if(strcmp(code,ch)==0)
		return true;
	else
		return false;
}
int in_HM(Code &c)
{
	c.data=new char[256];
	c.HM=new char* [256];
	int length=0,i;
	ifstream infile("code.dat",ios::in);
	if(!infile)
	{
		cerr<<"open error"<<endl;
		exit(1);
	}
	infile>>length;
	for(i=0;i<length;i++)
	{
		infile>>c.data[i];
		c.HM[i]=new char[length+1];
		infile>>c.HM[i];
	}
	infile.close(); 
	return length;	
}
void out_HM(HuffmanCode HC,TNode T)
{
	int i;	
	ofstream outfile("code.dat",ios::out);
	if(!outfile)
	{
		cerr<<"open error"<<endl;
		exit(1);
	}
	outfile<<T.length<<endl;
	for(i=0;i<T.length;i++)
	{
		outfile<<T.data[i]<<endl;
		outfile<<HC[i+1]<<endl;
	}
	outfile.close();
}
void RZip(HuffmanCode HC,TNode T)
{
	char ch;
	char ch2[30];
	int i,j,flag,flag2=0,m=0;
	ofstream outfile("rzip.txt",ios::out);//将压缩好的文件重新解压为rzip.txt
	ifstream infile("com.zip",ios::in);
	if(!outfile)
	{
		cerr<<"open error"<<endl;
		exit(1);
	}
	if(!infile)
	{
		cerr<<"open error"<<endl;
		exit(1);
	}
	while(infile.peek()!=EOF)
	{	
		flag=0;
		char* cd=new char[T.length];
		for(i=0;;i++)
		{
			infile>>ch;
			cd[i]=ch;
			cd[i+1]='\0';
			for(j=1;j<=T.length;j++)
				if(strcmp(HC[j],cd)==0)
				{
					if(T.data[j-1]=='@')
					{	
						if(flag2==0)
						{
							flag=1;
							flag2=1;
							delete cd;
							break;
						}
						if(flag2==1)
						{
							while(1)
							{
								if(REncode(ch2))
								{
									cout<<"密码正确,正在解压… "<<endl;
									flag2=0;
									break;
								}						
								else
									cout<<"密码错误,请重新输入"<<endl; 
							} 
							flag=1;
							delete cd;
							break;
						}	
					}
					if(flag2==1)
					{
						ch2[m]=T.data[j-1];
						flag=1;
						m++;
						delete cd;
						break;	
					}
					if(flag2==0)
					{
						outfile<<T.data[j-1];
						flag=1;
						delete cd;
						break;
					}
				}
			if(flag==1)
				break;
		}
	}
	cout<<"正在解压…";Sleep(500);cout<<" 。";Sleep(500);cout<<" 。"<<endl;
	cout<<"解压成功!请到rzip.txt中查看解压后文件" <<endl; 
}
void RZip()
{
	char ch;
	char ch2[30];
	Code c;
	int i,j,length,flag,flag2=0,m=0;
	length=in_HM(c);
	ofstream outfile("rzip.txt",ios::out);
	ifstream infile("com.zip",ios::in);
	if(!outfile)
	{
		cerr<<"open error"<<endl;
		exit(1);
	}
	if(!infile)
	{
		cerr<<"open error"<<endl;
		exit(1);
	}
	while(infile.peek()!=EOF)
	{	
		flag=0;
		char* cd=new char[length];
		for(i=0;;i++)
		{
			
			infile>>ch;
			cd[i]=ch;
			cd[i+1]='\0';
			for(j=0;j<length;j++)
				if(strcmp(c.HM[j],cd)==0)
				{
					if(c.data[j]=='@')
					{	
						if(flag2==0)
						{
							flag=1;
							flag2=1;
							delete cd;
							break;
						}
						if(flag2==1)
						{
							while(1)
							{
								if(REncode(ch2))
								{
									cout<<"密码正确,正在解压… "<<endl;
									flag2=0;
									break;
								}						
								else
									cout<<"密码错误,请重新输入"<<endl; 
							} 
							flag=1;
							delete cd;
							break;
						}	
					}
					if(flag2==1)
					{
						ch2[m]=c.data[j];
						flag=1;
						m++;
						delete cd;
						break;	
					}
					if(flag2==0)
					{
						outfile<<c.data[j];
						flag=1;
						delete cd;
						break;
					}
				}
			if(flag==1)
				break;
		}
	}
	cout<<"正在解压…";Sleep(500);cout<<" 。";Sleep(500);cout<<" 。"<<endl;	
	cout<<"解压成功,请到rzip.txt中查看解压后的文件" <<endl; 
}
void about()
{
	system("cls");
	cout<<"\t╔═══════════════════════════════╗"<<endl;
	cout<<"\t║                    欢迎使用解压缩程序   ^-^                  ║"<<endl;
	cout<<"\t║  关于程序:                                                  ║"<<endl;
	cout<<"\t║      压缩:                                                  ║"<<endl;	
	cout<<"\t║       默认压缩程序所在目录下的vae.txt,生成压缩后的com.zip   ║"<<endl;
	cout<<"\t║                                                              ║"<<endl;
	cout<<"\t║      解压:                                                  ║"<<endl;
	cout<<"\t║       默认解压序所在目录下的com.zip,生成解压后的rzip.txt    ║"<<endl;
	cout<<"\t║                                                              ║"<<endl;	
	cout<<"\t║      无源文件解压:                                          ║"<<endl;
	cout<<"\t║        使用仅以压缩文件解压,无需源文件,仅支持无空格无回车  ║"<<endl;
	cout<<"\t║        的文本解压!                                          ║"<<endl;
	cout<<"\t║                                                              ║"<<endl;
	cout<<"\t║      程序支持压缩中英文文件。                                ║"<<endl;
	cout<<"\t║                                                              ║"<<endl;
	cout<<"\t╚═══════════════════════════════╝"<<endl;
}
void menu()
{
	cout<<"\t╔═══════════════════════════════╗"<<endl;
	cout<<"\t║                                                              ║"<<endl;
	cout<<"\t║                  欢迎使用解压缩程序                          ║"<<endl;
	cout<<"\t║                                                              ║"<<endl;
	cout<<"\t║                     1. 压缩文件                              ║"<<endl;
	cout<<"\t║                     2. 解压文件                              ║"<<endl;
	cout<<"\t║                     3. 无源文件解压                          ║"<<endl;	
	cout<<"\t║                     4. 关于程序                              ║"<<endl;
	cout<<"\t║                     5. 退出程序                              ║"<<endl;
	cout<<"\t║                                                              ║"<<endl;
	cout<<"\t╚═══════════════════════════════╝"<<endl;
} 
int main(){
	vector<char> v;
	int n,choose;
	TNode T;
	InitList(T);
	HuffmanTree HT;	
	HuffmanCode HC;	
	while(1)
	{
		system("cls"); 
		menu();
		cout<<"请输入所选择的序号:";
		cin>>choose;
		switch(choose)
		{
			case 1:
				ReadTxt(v);
				TCount(v,T);
				n=T.length;
				CreateHuffmanTree (HT,T,n);
				CreatHuffmanCode (HT,HC,n);
				Zip(HC,v,T);
				out_HM(HC,T);
				system("pause");
				break;
			case 2:
				RZip(HC,T);
				system("pause");
				break;
			case 3:
				RZip();
				system("pause");
				break;
			case 4:
				about();
				system("pause");
				break; 
			case 5:
				return 0;
			default:
				cout<<"输入错误,请重新输入";
				system("pause");
				break; 		
		}
	}
	return 0;
 }

实验步骤:

运行选项4进入使用说明

原理:

哈夫曼编码的思想对文件进行压缩,主要原理是通过哈夫曼编码来重新表示字符,使得出现频率高的字符编码短,出现少的字符编码长。

压缩过程:

1)统计字符种类及频度:ASCII码值当下标,字符频度当对应下标的值。
2)构建哈夫曼树:在没有访问过的节点中,找最小字符频度下标来构建哈夫曼树。
3)构建哈夫曼编码:递归思想构建。
4)生成压缩文件(.ycyHuf后缀名):把字符的哈夫曼编码以二进制形式写入目标文件中。给压缩文件头部写入元数据,解压缩时需使用这些数据。

解压过程:

1)根据压缩文件的头部元数据,得到字符种类和频度。
2)构建哈夫曼树:在没有访问过的节点中,找最小字符频度下标来构建哈夫曼树。
3)构建哈夫曼编码:递归思想构建。
4)生成解压缩的文件(后缀名和源文件后缀名一样 )即可:一位一位的从压缩文件中读取信息,'1’向左子树走,'0’向右子树走。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值