图的最小生成树及最短路径算法
图的最小生成树算法及最短路径算法
——Prim算法,Dijkstra算法及Floyd算法
概述
我的上一篇博客图的DFS与BFS(C++)中谈到了用C++实现图的构建和遍历。这次,我仍然准备构建储存结构为邻接矩阵的无向图,来实现求图的最小生成树的Prim算法,求图单源最短路径的Dijkstra算法,以及求图多源最短路径的Floyd算法。其实求最小生成树还有一种Kruskal算法,但因为此例用邻接矩阵储存,不方便表达边与两个邻接点及权值的关系,故之后我会另开一篇叙述。
代码
1.引入必要头文件,对图的抽象数据类型进行声明。这里定义3个接口,分别对应用于实现3个算法。
#include<iostream>
#include<vector>
#include<mem.h>
int inf=9999;
using namespace std;
//Graph为封装的类模板
template <class T>
struct Graph{
//储存顶点
vector<T> vertex;
//储存边
vector<vector<int>> edge;
//标记数组
bool book[100];
Graph(int n,int m);
~Graph();
void Prim(int cur);
void Dijkstra(int cur);
void Floyd();
};
2.图的构造函数和析构函数:
template <class T>
Graph<T>::Graph(int n,int m){
vertex.resize(n+1);
edge.resize(n+1);
for(int i=0;i<n+1;i++){
edge[i].resize(n+1);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) edge[i][j]=0;
else edge[i][j]=inf;
}
}
cout<<"请依次输入各条边的两个邻点及权值(无向)"<<endl;
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
edge[a][b]=edge[b][a]=c;
}
cout<<endl;
for(int i=1;i<=n;i++){
vertex[i]=i; //顶点值用编号赋值,方便理解
}
memset(book,0,100);
}
template <class T>
Graph<T>:: ~Graph(){
vertex.clear();
edge.clear();
}
3.Prim算法实现
template <class T>
void Graph<T>::Prim(int cur){
vector<int> dis(vertex.size());
for(int i=1;i<=vertex.size()-1;i++){
dis[i]=edge[cur][i];
}
//表示1号顶点已加入最小生成树
int count=1;
cout<<vertex[cur]<<"——";
book[cur]=1;
int sum=0;
while(count<vertex.size()-1){
int min=inf;
int j;
for(int i=1;i<=vertex.size()-1;i++){
if(book[i]==0&&dis[i]<min){
min=dis[i];
j=i;
}
}
cout<<vertex[j];
sum += dis[j];
book[j]=1;
for(int k=1;k<=vertex.size()-1;k++){
if(book[k]==0&&edge[j][k]<dis[k]){
dis[k]=edge[j][k];
}
}
count++;
if(count<vertex.size()-1) cout<<"——";
}
cout<<endl;
cout<<"最小生成树权值和为:"<<sum<<endl;
}
4.Floyd算法实现
template <class T>
void Graph<T>::Floyd(){
/*设下标为k的顶点为从i到j的路径中途可能经过的点
如果该点而能使i到j路径收缩,则更新i到j路径 */
for(int k=1;k<=vertex.size()-1;k++){
//i,j为任意路径的起始点和终点
for(int i=1;i<=vertex.size()-1;i++){
for(int j=1;j<=vertex.size()-1;j++){
//路径收缩
if(edge[i][k]+edge[k][j]<edge[i][j]){
edge[i][j]=edge[i][k]+edge[k][j];
}
}
}
}
//打印出任意两点间的最短路径
for(int i=1;i<=vertex.size()-1;i++){
for(int j=1;j<=vertex.size()-1;j++){
if(edge[i][j]<inf&&edge[i][j]!=0){
cout<<i<<"->"<<j<<"最短路径长度为 "<<edge[i][j]<<endl;
}
}
}
}
5.Dijkstra算法实现
template <class T>
void Graph<T>::Dijkstra(int cur){
vector<int> dis(vertex.size());
for(int i=1;i<=vertex.size()-1;i++){
dis[i]=edge[cur][i];
}
int j;
//表示1号顶点已加入路径确定的集合中
book[1]=1;
while(count<vertex.size()-1){
int min=inf;
for(int i=1;i<=vertex.size()-1;i++){
if(book[i]==0&&dis[i]<min){
min=dis[i];
j=i;
}
}
book[j]=1;
for(int k=1;k<=vertex.size()-1;k++){
if(edge[j][k]+dis[j]<dis[k]&&edge[j][k]<inf){
dis[k]=dis[j]+edge[j][k];
}
}
count++;
}
for(int i=1;i<=vertex.size()-1;i++){
cout<<cur<<"->"<<i<<"最短路径长度为"<<dis[i]<<endl;
}
}
6.最后,main函数实例化图对象,调用对象方法,实现算法。(注意:每次调用用函数接口后要将标记数组重新初始化为0)
int main(){
Graph<int>* G=new Graph<int>(5,5);
cout<<"Prim算法求最小生成树如下:"<<endl;
G->Prim(1);
memset(G->book,0,100);
cout<<endl;
cout<<"Dijkstra算法求单源最短路径如下:"<<endl;
G->Dijkstra(1);
memset(G->book,0,100);
cout<<endl;
cout<<"Floyd算法求任意两点最短路径如下:"<<endl;
G->Floyd();
cout<<endl;
system("pause");
return 0;
}
输出
数学是符号的艺术,音乐是上界的语言。