哈夫曼編/譯碼器
【問題描述】
利用哈夫曼編碼進行通信可以大大提高信道利用率,縮短信息傳輸時間,降低傳輸成 本。但是,這要求在發送端通過一個編碼系統對待傳數據預先編碼,在接收端將傳來的數據進行譯碼(復原)。對於雙工信道(即可以雙向傳輸信息的信道),每端都需要一個完整的編/譯碼系統。試為這樣的信息收發站寫一個哈夫曼碼的編/譯碼系統。
【基本要求】
一個完整的系統應具有以下功能:
(1)I:初始化(Initialization)。從終端讀入字符集大小n , 以及n個字符和n個權值,建立哈夫曼樹,並將它存於文件hfmTree中。
(2)E:編碼(Encoding)。利用已建好的哈夫曼樹(如不在內存,則從文件hfmTree中讀人),對文件ToBeTran中的正文進行編碼,然后將結果存入文件CodeFile中。
(3)D: 譯碼(Decoding)。利用已建好的哈夫曼樹將文件 CodeFile 中的代碼進行譯碼,結果存入文件TextFile中。
(4)P:打印代碼文件(Print)。將文件CodeFile以緊湊格式顯示在終端上,每行 50 個代碼。同時將此字符形式的編碼文件寫入文件 CodePrin 中。
【測試數據】
用下表給出的字符集和頻度的實際統計數據建立哈夫曼樹 , 並實現以下報文的編碼和譯碼:"THIS PROGRAM 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 |
|
#include<iostream> #define INFINITY 10000//最大數 using namespace std; const int maxEdges=50; const int maxVertices=100; //最大頂點數 struct PathWay{ double dist;//權值 }; class MyGraph { private: int vNum;//當前頂點數 int eNum;//當前邊數 char Vertex[maxVertices];//頂點數組 PathWay Edge[maxVertices][maxVertices];//邊數組 public: void Create(int size = maxEdges);//構造函數 bool FindVertex(int vertex); bool InsertVertex(int vertex);//插入一個頂點vertex bool InsertEdge(const int v1,const int v2,const int weight);//插入一條邊(v1,v2),該邊上的權值為weight bool GetVertexPos(const int &vertex,int &i);//給出頂點vertex在圖中的位置 void Floyd();//Floyd算法 void Print();//輸出鄰接矩陣 void short_compare(double farthest[]);//求出最短路徑 double far_compare(double shortest[]);//這個問題的答案,在short_compare函數中選出一個最小的值 }; void MyGraph::Create(int size)//將圖轉化成鄰接矩陣保存 { int n,e;//n是村庄數,e是邊數 char name,tail,head; int weight; for(int i=0;i<size;i++){ for(int j=0;j<size;j++) { if(i==j){ Edge[i][j].dist=0;//初始化頂點到自身權值為0 } else{ Edge[i][j].dist=INFINITY;//初始化鄰接矩陣其余頂點為最大值 } } } cout<<"請輸入總頂點數(村庄):"; cin>>n; while(cin.fail()){ cin.clear(); char temp[100]; cin.getline(temp,100); cout<<endl; cout<<"Error:您輸入的不是數字."<<endl<<"請重新輸入:"; cin>>n; cout<<endl; } cout<<"請輸入總邊數(路徑):"; cin>>e; while(cin.fail()){ cin.clear(); char temp[100]; cin.getline(temp,100); cout<<endl; cout<<"Error:您輸入的不是數字."<<endl<<"請重新輸入:"; cin>>e; cout<<endl; } cout<<endl; cout<<"請輸入頂點名稱:"<<endl; for(int i=0;i<n;i++)//依次輸入頂點,插入圖中 { cout<<"請輸入第"<<i+1<<"個頂點(村庄)的名稱:"; cin>>name; InsertVertex(name); vNum++; } cout<<endl; cout<<"請輸入頭尾兩個頂點(村庄)和權值(路徑)[分別用空格隔開]:"<<endl; for(int i=0;i<e;i++){ cin>>head>>tail>>weight; if(!InsertEdge(head,tail,weight)) { cout<<"不存在該邊,請重新輸入這條邊:"; i--; continue; } } cout<<endl; } void MyGraph::short_compare(double farthest[]){ //比較數組(當前村庄和其他村庄間最短距離的集合中最大值的集合)中的最小值。選擇使離醫院最遠的村庄到醫院的路程最短 double route=farthest[0]; int point=0; for (int i=0; i<vNum; i++) { if (farthest[i]<route) { route=farthest[i]; point=i; } } cout<<"所以醫院應該建在村庄:"<<Vertex[point]<<endl; } double MyGraph::far_compare(double s[]){//得到最遠村庄的距離 double max=s[0]; int k; for(k=0;k<vNum;k++){ if(s[k]>max){ max=s[k]; } } return max; } bool MyGraph::FindVertex(int vertex)//給出頂點vertex在圖中的位置,這里也可以用來判斷在插入的時候是否已經有頂點了 { for (int i = 0; i < vNum; i++){ if (vertex == Vertex[i]){ return true; } } return false; } bool MyGraph::InsertVertex(int vertex)//插入頂點 { if (FindVertex(vertex)){ return false; } else{ Vertex[vNum] = vertex; return true; } } bool MyGraph::GetVertexPos(const int& vertex,int &i)//這個點是否在這條邊上 { for (i = 0; i < vNum; i++){ if (vertex == Vertex[i]){ return true; } } return false; } bool MyGraph::InsertEdge(int v1,int v2,int weight)//插入一條邊 { int k=0,j=0; if(GetVertexPos(v1,k) && GetVertexPos(v2,j)) { Edge[k][j].dist=weight; eNum++; Edge[j][k].dist=weight; eNum++; return true; } else{ return false; } } void MyGraph::Floyd() { double map[maxVertices][maxVertices],shortDistance[maxVertices],longDistance[maxVertices];//longDistance就是我們問題的答案 //map存放其他村庄到當前村庄的路徑和,shortDistance當前村庄到最遠村庄的最短距離,longDistance是shortDistance中的最大距離 int current,start,mid,i,j,k; j=k=0; for (current=0; current<vNum; current++){ for (start=0; start<vNum; start++) { map[current][start] = Edge[current][start].dist; } }//各對結點之間初始已知路徑及距離 for (mid=0; mid<vNum; mid++){ if(vNum-1 == mid){ //這里看插入的點是否等於頂點數減一,如果小於的話證明還沒插完整,加上前面的繼續插,不可能大於,然后插到倒數第一個,比方說 //我們這個題有6個點,則插五個點就夠了,排除自己的。然后當插到第五個點的時候我們就可以判斷第五個點插進去是否小於之前的,然后輸出他的距離。 for(current=0; current<vNum; current++){ k=0; shortDistance[k]=0; for (start=0; start<vNum; start++){ if (map[current][mid] + map[mid][start] < map[current][start]){ map[current][start] = map[current][mid] + map[mid][start]; } if(current != start){//這里要杜絕自己到自己的路徑 cout<<"村庄"<<Vertex[current]<<"到村庄"<<Vertex[start]<<"的最短距離為:"<<map[current][start]<<endl; shortDistance[k] = map[current][start]; k++; } } longDistance[j] = far_compare(shortDistance); cout<<"村庄"<<Vertex[current]<<"到最遠的村庄的最短距離為:"<<longDistance[j]<<endl; j++; cout<<endl; } }else{//沒有插完整的話繼續插 for (current=0; current<vNum; current++){ for (start=0; start<vNum; start++){ if (map[current][mid] + map[mid][start] < map[current][start]){ map[current][start] = map[current][mid] + map[mid][start]; } } } } } short_compare(longDistance); } void MyGraph::Print(){ int i,j,count=0; cout<<"所得圖的鄰接矩陣為:"<<endl; for(i=0;i<vNum;i++){ for(j=0;j<vNum;j++){ if(Edge[i][j].dist!=INFINITY){ cout<<Edge[i][j].dist<<"\t"; } else if(i==j){ cout<<0<<"\t"; } else{ cout<<"∞"<<"\t"; } count++; } if(count%vNum==0){ cout<<endl; } } cout<<endl; } int main() { MyGraph Hospital; Hospital.Create(maxVertices); Hospital.Print(); Hospital.Floyd(); return 0; }
哈夫曼編碼的流程:
哈夫曼編碼譯碼系統的流程:
具體的哈夫曼樹: