利用贪心算法求解单源最短路径问题,最著名的算法是Dijkstra算法,本文便简单介绍一下该算法。
算法整体思想:按各个顶点与源点之间的路径长度的递增次序,生成源点到各个顶点的最短路径。即先求出长度最短的一条路径,再参照它求出长度次短的一条,以此类推,直到从源点到其他各个顶点的最短路径全部求出为止,俗称迪杰斯特拉算法。
算法设计:设源点为u,顶点集合V被分为两部分,S和V-S,其中S中的顶点到源点的最短路径已经确定,V-S中未确定。该算法选用的贪心策略是:每次从V-S集合中选择一个当前离源点的路径最短的顶点,并将其加入集合S中。
求解步骤:
①设置带权邻接矩阵C[][],一维数组dis[]用来存储最短路径长度,一维数组p[]用来存储最短路径中i顶点的前驱顶点,即p[i]的值为i顶点的前驱顶点。记录顶点是否在S集合中的bool类型的数组res[]。若顶点i在集合S中,res[i]=true,反之为false。
②初始化。res[u]=true; 对V-S中的所有顶点x,设置dist[x]= C[u][x]。如果顶点x与源点u相邻,初始化p[x]为u,如果不相邻,也就是C[u][x]= ∞,初始化p[x]为-1。
③在V-S中依照贪心策略来寻找使得dist[x]具有最小值的顶点tv,满足该条件的顶点就是V-S中距离源点u最近的顶点。
④将tv加入S中,即设置res[tv]=true。
⑤如果V-S集合为空,算法结束。否则转步骤⑥。
⑥对集合V-S中的所有与顶点tv相邻的顶点x,更新其到源点的最短路径长度,即如果dist[x] > dist[tv] + C[tv][x],设置dist[x] = dist[tv] + C[tv][x],并设置p[x]=tv。转步骤③。
由此可求得源点u到该有向带权图的其余各个顶点的最短路径及其长度。
构造实例:
图1 有向带权图G
表1 邻接矩阵
表2 算法求解过程
表3 前驱数组p变化情况
代码实现:
#include <iostream>
using namespace std;
void DA (int n,int u,int *dist,int *p,int C[][5])
{
bool res[n];
for(int i=0;i<n;i++)
{
dist[i]=C[u][i]; //首先将每个顶点到源点的最短距离初始化为从源点出发,到每个顶点的直接距离(没有边的为无穷,可用INT_MAX代替)
res[i]=false; //全部顶点放到集合V-S中
if(dist[i]==INT_MAX) //若源点无边到该顶点
p[i]=-1; //将路径设为-1
else
p[i]=u; //否则设为源点
}
res[u]=true;
for(int i=0;i<n;i++)
{
int tv =u;
int temp = INT_MAX;
for(int j=0;j<n;j++)
{
if((!res[j])&&(dist[j]<temp)) //每次从剩余顶点中找出一个当前距离源点最近的顶点
{
temp=dist[j];
tv=j;
}
}
if(tv==u)break;
res[tv]=true;//将顶点tv放入S集合中
for(int j=0;j<n;j++) //每放入S一个顶点,就要将与tv顶点相连的剩余顶点的到源点最短路径更新一下
{
if((!res[j])&&(C[tv][j]!=INT_MAX))
if(dist[j]>dist[tv]+C[tv][j])
{
dist[j]=dist[tv]+C[tv][j];
p[j]=tv; //将剩余顶点的路径设为tv
}
}
}
}
int main()
{
int A[5][5]={ 0,8,32,INT_MAX,INT_MAX,
12,0,16,15,INT_MAX,
INT_MAX ,29 ,0,INT_MAX,13,
INT_MAX,21,INT_MAX,0,7,
INT_MAX,INT_MAX,27,19,0 };
int dist[5];
int p[5];
DA(5,0,dist,p,A);
for(int i=0;i<5;i++)
cout << dist[i] << " "<<p[i]<<endl;
return 0;
}