数据结构实验——基于哈夫曼树的数据压缩算法

/*

注:输入为多行字符串,以“0”结尾;例:abc def 0

此程序无法执行由单个字符组成的字符串。

*/

#include<iostream>

#include<string>
#include<map>
using namespace std;


typedef struct
{
int weight;
int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef char **HuffmanCode;


void Select(HuffmanTree HT,int len,int &s1,int &s2)
{
int i,min1=0x3f3f3f3f,min2=0x3f3f3f3f;//先赋予最大值
for(i=1;i<=len;i++)
{
if(HT[i].weight<min1&&HT[i].parent==0)
{
min1=HT[i].weight;
s1=i;
}
}
int temp=HT[s1].weight;//将原值存放起来,然后先赋予最大值,防止s1被重复选择
HT[s1].weight=0x3f3f3f3f;
for(i=1;i<=len;i++)
{
if(HT[i].weight<min2&&HT[i].parent==0)
{
min2=HT[i].weight;
s2=i;
}
}
HT[s1].weight=temp;//恢复原来的值
}


//用算法5.10构造赫夫曼树
void CreatHuffmanTree(HuffmanTree &HT,int n,map<char,int> & maps)
{
//构造赫夫曼树HT
int m,s1,s2,i;
if(n<=1) return;
m=2*n-1;
HT=new HTNode[m+1];   //0号单元未用,所以需要动态分配m+1个单元,HT[m]表示根结点   
for(i=1;i<=m;++i)         //将1~m号单元中的双亲、左孩子,右孩子的下标都初始化为0   
  { HT[i].parent=0;  HT[i].lchild=0;  HT[i].rchild=0; }




map<char,int>::iterator it;
it=maps.begin();
for(i=1;i<=n;++i,it++)         //输入前n个单元中叶子结点的权值  
HT[i].weight=it->second; 
/*――――――――――初始化工作结束,下面开始创建赫夫曼树――――――――――*/ 
for(i=n+1;i<=m;++i) 
{   //通过n-1次的选择、删除、合并来创建赫夫曼树
Select(HT,i-1,s1,s2);
//在HT[k](1≤k≤i-1)中选择两个其双亲域为0且权值最小的结点,
// 并返回它们在HT中的序号s1和s2
HT[s1].parent=i;
HT[s2].parent=i;   
//得到新结点i,从森林中删除s1,s2,将s1和s2的双亲域由0改为i
HT[i].lchild=s1;   
HT[i].rchild=s2 ; //s1,s2分别作为i的左右孩子
HT[i].weight=HT[s1].weight+HT[s2].weight; //i 的权值为左右孩子权值之和
} //for
}
// CreatHuffmanTree
void CreatHuffmanCode(HuffmanTree HT,HuffmanCode &HC,int n)
{
//从叶子到根逆向求每个字符的赫夫曼编码,存储在编码表HC中
int i,start,c,f;
HC=new char*[n+1];         //分配n个字符编码的头指针矢量
char *cd=new char[n]; //分配临时存放编码的动态数组空间
cd[n-1]='\0';                             //编码结束符
for(i=1;i<=n;++i)
{                       //逐个字符求赫夫曼编码
start=n-1;                           //start开始时指向最后,即编码结束符位置
c=i; 
f=HT[i].parent;                 //f指向结点c的双亲结点
while(f!=0)
{                           //从叶子结点开始向上回溯,直到根结点
--start;                           //回溯一次start向前指一个位置
if(HT[f].lchild==c)  
cd[start]='0'; //结点c是f的左孩子,则生成代码0
else 
cd[start]='1';                 //结点c是f的右孩子,则生成代码1
c=f; 
f=HT[f].parent;             //继续向上回溯
}                                   //求出第i个字符的编码      
HC[i]=new char[n-start];         // 为第i 个字符编码分配空间
strcpy(HC[i], &cd[start]);         //将求得的编码从临时空间cd复制到HC的当前行中
}
delete cd;                             //释放临时空间
}




void select_yuan_su(string a1,map<char,int> & maps,int &n,string &h)
{
map<char,int>::iterator it,is;

for(int i=0;i<a1.length();i++)
{
it = maps.find(a1[i]);
if(it != maps.end())  
(it->second)++;
            else
{ maps[ a1[i] ] = 1; n++;}
}


is=maps.end();
is--;

for(it=maps.begin();it!=maps.end();it++)
{
h+=it->first;
cout<<it->first<<":"<<it->second;
if(it!=is) cout<<" ";
}
cout<<endl;
}




void shuchu_zhongtai(HuffmanTree ht,int n)
{
for(int i=1;i<2*n;i++)
{
cout<<i<<" "<<ht[i].weight<<" "<<ht[i].parent
<<" "<<ht[i].lchild<<" "<<ht[i].rchild<<endl;
}


}




void shuchu_bian_ma(HuffmanCode hc,int n,map<char,int> &maps)
{
    map<char,int>::iterator it,is;


it=maps.begin();
is=maps.end();
is--;
    for(int i=1;i<=n;++i,it++) 
{
cout<<it->first<<":"<<hc[i];
if(it!=is) cout<<" ";
}
cout<<endl;


}




string bian_ma(string a1,string h,HuffmanCode hc,int n)
{
string m;
char m2;
for(int i=0;i<a1.length();i++)
{
m2=a1[i];
for(int j=0;j<n;j++)
{
if(m2==h[j]) 
{
m+=hc[j+1];
break;

}

}
cout<<m<<endl;
return m;
}




void jie_ma(string m,string h,HuffmanCode hc,int n)
{


string y,x;
for(int i=0;i<m.length();i++)
{
y+=m[i];
for(int j=1;j<=n;j++)
{
if(y==hc[j]) 
{
x+=h[j-1];
y.erase();
break;

}
}
cout<<x<<endl;
}





void main()
{
string a1;
string b1="0";


cin>>a1;

while(a1!=b1)
{
map<char,int> maps;
int i,n;
string h;
n=0;
select_yuan_su(a1,maps,n,h);




HuffmanTree ht;
   HuffmanCode hc;


CreatHuffmanTree(ht,n,maps);
shuchu_zhongtai(ht,n);



   CreatHuffmanCode(ht,hc,n);
shuchu_bian_ma(hc,n,maps);




string m=bian_ma(a1,h,hc,n);


jie_ma(m,h,hc,n);




cin>>a1;
}


}

综合实验: 1. 问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。这要求在发送端通过一个编码系统对待传输数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站编写一个哈夫曼码的编/译码系统。 2. 基本要求 一个完整的系统应具有以下功能: (1) I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。 (2) E:编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。 (3) D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件Textfile中。 (4) P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。 (5) T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(比如树)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint 中。 3. 测试数据 用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAME IS MY FAVORITE”。 字符 A B C D E F G H I J K L M 频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20 字符 N O P Q R S T U V W X Y Z 频度 57 63 15 1 48 51 80 23 8 18 1 16 1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值