数据结构程序设计-哈夫曼编/译码器
哈夫曼编/译码器
【问题描述】
利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成 本。但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站写一个哈夫曼码的编/译码系统。
【基本要求】
一个完整的系统应具有以下功能:
(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; }
哈夫曼编码的流程:
哈夫曼编码译码系统的流程:
具体的哈夫曼树: