问题描述
利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。
算法思想
建立两个结构体,HTNode储存哈夫曼树信息,frequence储存字符的相关信息。以#号键作为输入的结束条件,并用map记录各个字符的出现次数,保存下字符种类的总数,遍历map,给frequence类型的数组赋值,计算频数保存为哈夫曼树的权值。创建哈弗曼树时先对哈弗曼树的信息进行初始化,之后选择两个权值最小的结点,for循环,i从字符种类加一开始,将i赋给权值最小的两个结点的双亲,将权值最小的两个结点的序号分别赋给第i个结点的左孩子和右孩子,两个最小权值相加即为i的权值。编码是从叶子结点到根结点遍历,逐个求解n个字符的编码,循环n次。译码则是从根结点到叶子结点,逐个求解编码对应的n个字符,循环n次。
算法设计
1、菜单void menu()——输出功能选择。
2、记录输入的字符信息void record(HuffmanTree &HT,fre &fq)——以#号键作为输入的结束条件,并用map记录各个字符的出现次数,保存下字符种类的总数,遍历map,给frequence类型的数组赋值,计算频数保存为哈夫曼树的权值。
3、选择结点void Select(HuffmanTree &HT,int n,int &s1,int &s2)——选择两个权值最小的结点。
4、创建哈弗曼树void CreateHuffmanTree(HuffmanTree &HT)——创建哈弗曼树时先对哈弗曼树的信息进行初始化,之后选择两个权值最小的结点,for循环,i从字符种类加一开始,将i赋给权值最小的两个结点的双亲,将权值最小的两个结点的序号分别赋给第i个结点的左孩子和右孩子,两个最小权值相加即为i的权值。
5、编码void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,fre &fq)——从叶子结点到根结点遍历,逐个求解n个字符的编码,循环n次
6、译码void DeHuffmanCode(HuffmanTree HT,HuffmanCode &HC,fre &fq)——从根结点到叶子结点,逐个求解原码对应的字符,直到访问到’\0’。
7、主函数int main()——使用while循环和switch对功能进行调用,使用清屏函数,使界面更理想。
代码实现
#include<stdio.h>//实验三
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
const int INT=100000000;
typedef struct{//定义哈弗曼树
int weight;//权值
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef struct{//统计频率
char c;//字符
int times;//字符出现次数
}frequence,*fre;
typedef char **HuffmanCode;
char s[51];
int sum=0;int length;
void menu(){
printf("***********哈弗曼编译码系统***********\n");
printf(" 1、编码系统\n");
printf(" 2、译码系统\n");
printf(" 3、退出\n");
printf("**************************************\n");
printf("请选择...\n");
}
void record(HuffmanTree &HT,fre &fq){//记录输入的字符信息
map<char,int>m;//用map获得字符出现次数
printf("请输入字符信息(以#号键结束输入):");
int i;
for(i=0;;++i)
{
scanf("%c",&s[i]);
if(s[i]=='#'){//输入结束的控制条件
s[i]='\0';
break;
}
else{
m[s[i]]++;
}
}
length=i;//记录字符总数
sum=m.size();//记录字符种类总数
int j=1;
fq=new frequence[sum+1];
HT=new HTNode[2*sum+1];
map<char,int>::iterator iter;
cout<<"字符种类数为:"<<m.size()<<endl;
for(iter=m.begin();iter!=m.end();iter++){
cout<<"字符"<<iter->first<<": ";
cout<<iter->second<<"次"<<endl;
fq[j].c=iter->first;
fq[j].times=iter->second;
HT[j].weight=100*fq[j].times/length;//储存字符出现的频数
j++;
}
}
void Select(HuffmanTree &HT,int n,int &s1,int &s2){//选择两个权值最小的结点
int s11=INT+1;
int s22=INT+1;
for(int j=1;j<=n;j++){
if(HT[j].parent==0&&s11>HT[j].weight)
{
s11=HT[j].weight;
s1=j;
}
}
for(int j=1;j<=n;j++){
if(j==s1)
continue;
if(HT[j].parent==0&&s22>HT[j].weight&&s11<=HT[j].weight)
{
s22=HT[j].weight;
s2=j;
}
}
}
void CreateHuffmanTree(HuffmanTree &HT){//创建哈弗曼树
int i;
int s1=1,s2=1,w=sum;
for(i=1;i<=(2*w-1);i++){
HT[i].parent=0;//初始化
HT[i].lchild=0;
HT[i].rchild=0;
if(i>w)
HT[i].weight=INT+1;
}
cout<<endl<<"哈弗曼树编码次序为:"<<endl;
for(i=w+1;i<=(2*w-1);++i){
Select(HT,i-1,s1,s2);
HT[s1].parent=i;//将i赋给权值最小的两个结点的双亲
HT[s2].parent=i;
HT[i].lchild=s1;//将权值最小的两个结点的序号分别赋给第i个结点的左孩子和右孩子
HT[i].rchild=s2;
HT[i].weight=HT[s1].weight+HT[s2].weight;//两个最小权值相加即为i的权值
cout << "HT[" << s1 << "] and HT[" << s2 << "] create";
cout << " HT[" << i << "], weight=" << HT[i].weight << endl;
}
cout<<endl;
}
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,fre &fq){//编码
int n=sum;
HC=new char*[n+1];
char *cd;
cd=new char[n];
cd[n-1]='\0';
for(int i=1;i<=n;++i){
int start=n-1;
int c,f;
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]);
cout<<"字符"<<fq[i].c<<"的编码:"<<HC[i]<<endl;
}
cout<<endl;
cout<<"编码信息:";
for(int i=0;i<length;++i){
for(int j=1;j<=sum;j++)
if(s[i]==fq[j].c)
cout<<HC[j];
}
cout<<endl<<endl;
delete cd;
}
void DeHuffmanCode(HuffmanTree HT,HuffmanCode &HC,fre &fq)//译码
{
//cout<<"字符信息为:";
//printf("%s\n",s);
cout<<"请输入需要破译的原码:";
char late[51];
int p=2*sum-1,i=0;
scanf("%s",late);
cout<<"译码为:";
while (late[i] != '\0' && p != 0)
{
if (late[i] == '0')
p = HT[p].lchild; //走向左孩子
else
p = HT[p].rchild; //走向右孩子
if (!HT[p].lchild && !HT[p].rchild) //tree[i]是叶结点
{
cout << fq[p].c;
p = 2 * sum - 1; //回到根结点
}
i++;
}
cout << endl;
if (HT[i].lchild&&late[i]!= '\0')
cout << "\n输入原码错误\n";
}
int main(){
HuffmanTree HT;
HuffmanCode HC;
fre fq;
int m;
while(m!=3){
menu();
cin>>m;
getchar();
switch(m){
case 1:
record(HT,fq);
CreateHuffmanTree(HT);
CreateHuffmanCode(HT,HC,fq);
break;
case 2:
DeHuffmanCode(HT,HC,fq);
break;
case 3:
cout<<"感谢您的使用!"<<endl;
return 0;
default:
cout<<"没有该选项!"<<endl;
}
system("pause");
system("cls");
}
return 0;
}