问题描述:
在给定的带权有向图G=(V,E)中,求V集合的中源点v0到其余各点的最短路径。
思路:
设置一个集合S存放已经找到最短路径的顶点(初始时先将源点v0存入),
对于其余顶点,每次都会找一个从源点出发路径最短的顶点,就将其放入集合中,
最新放入集合的顶点会成为源点v0通往其他顶点的辅助顶点,
即源点v0可以借助集合中的其他顶点到达集合外的某一顶点,但不可以借助集合外的任意顶点到达其他任意顶点。
重复上述过程,直到所有顶点都放入集合中。
辅助变量:
- 定义一个数组dist,其下标对应顶点数组下标,表示每个顶点到源点v0之间的最短路径长度,不存在则设为无穷大。
- 定义一个数组path,其下标对应顶点数组下标,表示每个顶点与哪个顶点之间的路径最小。初始都设为-1。该数组用于最后打印最短路径,假设源点0到顶点1之间的最短路径为0→4→1,则可以通过数组path,先找到path[1],其值为与顶点1之间权值最小的顶点的下标,即4,则再找到path[4],其值为与顶点4之间权值最小的顶点的下标,即源点0,这样便获得了最短路径,可以看出这个过程是先进后出的过程,所以在获取路径的过程中可以用栈结构来存储。
- 定义一个数组s表示集合S,true代表该顶点在集合中,false代表该顶点不再集合中。
大致流程:
1、初始化三个数组
2、while(数组s的元素个数小于图的顶点个数)
2.1、在数组dist中求最小值,其下标为m
2.2、将顶点m添加到集合S中(即将数组s对应下标标记为true)
2.3、检查借助顶点m能否使源点到达各个顶点的路径更短,能则修改数组dist和path
代码实现:
//图结构的定义
typedef struct AMGraph
{
int vex[ARRAYSIZE]={0};//顶点数组
int arc[ARRAYSIZE][ARRAYSIZE];//邻接矩阵,表示顶点之间的关系
int vexnum=0,arcnum=0;//顶点个数和弧个数
int type=0;//1表示无向图,2表示有向图,3表示无向网,4表示有向网
}AMGraph;
int LocateVex(AMGraph &G,int e); //获取顶点位置,返回顶点e在顶点数组中的下标
//打印最短路径
void DisplayPath(AMGraph &G,vector<int> &path,vector<int> &dist,int v)
{
stack<int> s;
int p=0;
for(int i=0;i<G.vexnum;++i)
{
if(i==v)
{
continue;
}
if(dist[i]==INT_MAX)
{
cout << "源点" << v << "到顶点" << G.vex[i] << "不存在路径" << endl;
continue;
}
p=path[i];
s.push(p);
while(p!=v)
{
p=path[p];
s.push(p);
}
cout << "源点" << v << "到顶点" << G.vex[i] << "的最短路径为:";
while(!s.empty())
{
cout << G.vex[s.top()] << "→";
s.pop();
}
cout << G.vex[i] << endl;
cout << "路径长度为:" << dist[i] << endl;
}
}
//Dijskra算法
void Dijskra(AMGraph &G,int v)
{
if(G.type!=4)
{
cout << "图类型有误,该函数只适用于带权有向图" << endl;
return;
}
//获取源点下标
v=LocateVex(G,v);
if(v==-1)
{
cout << "图中不存在该顶点" << endl;
return;
}
//建立与初始化数组
vector<int> dist,path;
for(int i=0;i<G.vexnum;++i)
{
dist.push_back(G.arc[v][i]);
if(G.arc[v][i]!=INT_MAX)
path.push_back(v);
else
path.push_back(-1);
}
vector<bool> s(G.vexnum,false);
s[v]=true; //表示源点已处理,即源点在集合S中
int min=INT_MAX,m=-1;//用于存储dist数组的最小值及其下标
for(int n=1;n<G.vexnum;++n)
{
//寻找dist数组中的最小值
for(int i=0;i<G.vexnum;++i)
{
if(s[i]==true) continue;
if(dist[i]<=min)
{
min=dist[i];
m=i;
}
}
s[m]=true;//将找到的顶点标记已处理
//修改数组dist和path:
//检查每个未处理的顶点,
//检查“当前源点到各个顶点的长度”是否比“源点借助刚找到的顶点m到达各个顶点的长度”要长
//如果借助顶点m会使源点与某顶点之间的路径更短,则修改某顶点对应的dist值和path值
for(int i=0;i<G.vexnum;++i)
{
//dist[i]:顶点i到源点的长度;dist[m]:顶点m到源点的长度;G.arc[m][i]:顶点m到顶点i的长度.
//BUG记录:若G.arc[m][i]为无穷大,再添加dist[m]会溢出,导致算法出错
if(s[i]==false && G.arc[m][i]!=INT_MAX && dist[i]>dist[m]+G.arc[m][i])
{
dist[i]=dist[m]+G.arc[m][i];
path[i]=m;
}
}
min=INT_MAX;
m=-1;
}
//打印最短路径
DisplayPath(G,path,dist,v);
}