哈夫曼編碼/譯碼系統(樹應用)
[問題描述]
利用哈夫曼編碼進行通信,可以壓縮通信的數據量,提高傳輸效率,縮短信息的傳輸時間,還有一定的保密性。現在要求編寫一程序模擬傳輸過程,實現在發送前將要發送的字符信息進行編碼,然後進行發送,接收後將傳來的數據進行譯碼,即將信息還原成發送前的字符信息。
[實現提示]
在本例中設置發送者和接受者兩個功能,
發送者的功能包括:
①輸入待傳送的字符信息;
②統計字符信息中出現的字符種類數和各字符出現的次數(頻率);
②根據字符的種類數和各自出現的次數建立哈夫曼樹;
③利用以上哈夫曼樹求出各字符的哈夫曼編碼;
④將字符信息轉換成對應的編碼信息進行傳送。
接受者的功能包括:
①接收發送者傳送來的編碼信息;
②利用上述哈夫曼樹對編碼信息進行翻譯,即將編碼信息還原成發送前的字符信息。
從以上分析可發現,在本例中的主要算法有三個:
(1)哈夫曼樹的建立;
(2)哈夫曼編碼的生成;
(3)對編碼信息的翻譯。
[設計思路]
- 哈夫曼樹的建立
- 哈夫曼編碼的生成
- 對編碼信息的翻譯
[代碼及註釋]
#include<iostream>
#include<map>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXSIZE 111 /*可傳遞信息的最大長度*/
//結構
typedef struct{
int weight; /*結點的權值*/
int parent,lchild,rchild; /*結點的雙親、左孩子、右孩子的下標*/
char ch; /*結點的字符*/
string HuffmanCode; /*結點的哈夫曼編碼*/
}HTNode,*HuffmanTree; /*動態分配數組儲存哈夫曼樹*/
map<char,int> Map; /*map容器儲存字符及對應次數*/
//子函數
void GetInformation(int &kind); /*統計字符種類數和各字符出現的次數*/
void SelectMinNode(HuffmanTree &HT,int k,int &s1,int &s2);/*選擇兩個權值最小結點*/
bool CreatHuffmanTree(HuffmanTree &HT,int n); /*哈夫曼樹的創建*/
void CreatHuffmanCode(HuffmanTree &HT,int n); /*哈夫曼編碼的創建*/
void Translate(HuffmanTree HT,int n); /*譯碼*/
//具體內容
void GetInformation(int &kind)
{
kind=0;
cout<<"輸入傳遞信息(不含中文):";
char str[MAXSIZE];
gets(str); /*可讀入含空格道德字符串*/
for(int i=0;i<strlen(str);++i)
Map[str[i]]++; /*記錄字符出現次數*/
map<char,int> ::iterator it=Map.begin();
while(it!=Map.end())
{
++kind;
it++;
} /*迭代器遍歷*/
cout<<endl;
cout<<"字符種類:"<<kind<<endl<<endl;
cout<<" ---------"<<endl;
cout<<"|字符"<<" 次數"<<endl;
for(it=Map.begin();it!=Map.end();++it)
cout<<"| "<<(*it).first<<" "<<(*it).second<<endl;
cout<<" ---------"<<endl;
}
void SelectMinNode(HuffmanTree &HT,int k,int &s1,int &s2)
{
int Min=1e9; /*初始化當前最小權值*/
for(int i=1;i<=k;++i)
if(!HT[i].parent)
if(HT[i].weight<=Min)
{
Min=HT[i].weight; /*不斷更新Min*/
s1=i;
} /*從前k個結點找出最小結點*/
Min=1e9; /*重置權值*/
for(int i=1;i<=k;++i)
if(!HT[i].parent&&i!=s1)
if(HT[i].weight<=Min)
{
Min=HT[i].weight; /*不斷更新Min*/
s2=i;
} /*從前k個結點除s1外找出最小結點*/
}
bool CreatHuffmanTree(HuffmanTree &HT,int n)
{
if(n<=1)
{
cout<<"<<<<字符種類太少無需使用哈夫曼樹"<<endl;
return false;
}
else
{
int m=2*n-1;
HT=new HTNode[m+1]; /*分配空間,0號單元未使用*/
map<char,int> ::iterator it=Map.begin();
int j=0;
while(it!=Map.end())
{
++j;
HT[j].weight=(*it).second; /*初始化字符出現次數*/
HT[j].ch=(*it).first; /*初始化字符*/
HT[j].HuffmanCode=""; /*初始化哈夫曼編碼爲空*/
HT[j].lchild=HT[j].rchild=HT[j].parent=0; /*左右孩子及雙親均初始化爲0*/
++it;
}
for(int i=n+1;i<=m;++i)
HT[i].weight=HT[i].parent=HT[i].rchild=HT[i].lchild=0;
/*初始化從n+1到m的結點*/
for(int i=n+1;i<=m;++i) /*n-1次的選擇刪除合併來創建哈夫曼樹*/
{
int s1,s2;
SelectMinNode(HT,i-1,s1,s2); /*將最小兩個結點的下標賦予s1,s2;*/
HT[s1].parent=i; /*s1雙親親賦予i第i編號*/
HT[s2].parent=i; /*s2雙親親賦予i第i編號*/
HT[i].lchild=s1; /*s1成爲編號爲i的左孩子*/
HT[i].rchild=s2; /*s2成爲編號爲i的右孩子*/
HT[i].weight=HT[s1].weight+HT[s2].weight; /*合併左右孩子權值*/
}
cout<<"<<<<哈夫曼樹創建成功"<<endl<<endl;
return true;
}
}
void CreatHuffmanCode(HuffmanTree &HT,int n)
{
int c,f;
cout<<"<<<<哈夫曼編碼創建成功,如下:"<<endl;
for(int i=1;i<=n;i++)
{
c=i; /*c:當前結點序號*/
f=HT[i].parent; /*f:當前節點的雙親*/
while(f!=0) /*f爲根節點終止*/
{
if(HT[f].lchild==c)HT[i].HuffmanCode+="0"; /*左邊加0*/
else HT[i].HuffmanCode+="1"; /*右邊加1*/
c=f;
f=HT[f].parent;
/*不斷向上層查找*/
}
reverse(HT[i].HuffmanCode.begin(),HT[i].HuffmanCode.end());
/*翻轉,因爲上步操作得到的是逆序*/
cout<<HT[i].ch<<":"<<HT[i].HuffmanCode<<endl;
}
cout<<endl;
}
void Translate(HuffmanTree HT,int n)
{
cout<<"輸入譯碼:";
string str;
cin>>str;
int len=str.size();
int flag=-1; /*判斷譯碼是否成功的標誌*/
string ans,temp; /*ans:最終譯碼答案*/
/*temp:匹配臨時字符串*/
for(int i=0;i<len;++i)
{
temp+=str[i];
for(int j=1;j<=n;++j)
if(HT[j].HuffmanCode==temp)
{
temp=""; /*匹配成功將臨時字符串置空*/
ans+=HT[j].ch;
flag=i;
}
}
if(flag!=len-1)cout<<"<<<<輸入的源碼有誤!!!"<<endl; /*沒有匹配成功*/
else
{ cout<<"<<<<譯碼成功!內容如下"<<endl;
cout<<ans<<endl;
}
cout<<"是否繼續(y/n)?";
char judge; /*判斷是否繼續譯碼*/
cin>>judge;
if(judge=='y')Translate(HT,n);
else return;
}
int main()
{
HuffmanTree HT;
int kind;
GetInformation(kind);
if(CreatHuffmanTree(HT,kind))
{
CreatHuffmanCode(HT,kind);
Translate(HT,kind);
}
return 0;
}
[簡單展示]