从一个源点到其他各顶点的最短路径问题称为“单源最短路径问题”。
-
最短路径的最优子结构性质
- 该性质描述为:如果P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。
- 假设P(i,j)={Vi…Vk…Vs…Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P’(k,s),那么P’(i,j)=P(i,k)+P’(k,s)+P(s,j)<P(i,j)。则与P(i,j)是从i到j的最短路径相矛盾。因此该性质得证。
-
Dijkstra算法
- 定义dist[W] = S到W的最短距离;
dist[S] = 0;
path[W] = S到W路径上经过的顶点(即W前一个父顶点)。
- 由上述性质可知,如果存在一条从i到j的最短路径(Vi…Vk,Vj),Vk是Vj前面的一顶点。那么(Vi…Vk)也必定是从i到k的最短路径。为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。 譬如对于源顶点s,
- 选择其直接相邻的顶点中长度最短的顶点V,此时将V收录到集合S ={源点s + 已经确定了最短路径的顶点V}中;
- V进入集合S后,将有可能影响V一圈邻接点W的dist值,dist[W] = min{dist[W], dist[V] + <V, W>的权重};
- 最短路径找到,停止。
-
伪代码
void Dijkstra(Vertex V)
{
while (1) {
V = 未收录顶点中dist最小者;
if ( 这样的V不存在)
break;
collected[V] = true;
for ( V的每个邻接点W )
if (collected[W] == false && dist[V] + E<V, W> < dist[W] ){
dist[W] = dist[V] + E<V, W>;
path[W] = V;
}
}
}
-
算法复杂度
- FinMinDist可以用小顶堆实现。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <stack>
using namespace std;
#define MaxVertexNum 100
#define INFINITY 65535
typedef int Vertex;
typedef int WeightType;
typedef char DataType;
typedef struct GNode *PtrToGNode;
struct GNode {
int Nv;
int Ne;
WeightType G[MaxVertexNum][MaxVertexNum];
DataType Data[MaxVertexNum];
};
typedef PtrToGNode MGraph;
typedef struct ENode *PtrToENode;
struct ENode {
int V1, V2;
WeightType Weight;
};
typedef PtrToENode Edge;
MGraph CreateGraph(int VertexNum)
{
MGraph Graph = (MGraph)malloc(sizeof(struct GNode ));
Graph->Nv = VertexNum;
Graph->Ne = 0;
for (int i = 0; i < Graph->Nv ; i++){
for (int j = 0; j < Graph->Nv ; j++ )
Graph->G[i][j] = INFINITY;
}
return Graph;
}
void InsertEdge(MGraph Graph, Edge E)
{
Graph->G[E->V1 ][E->V2 ] = E->Weight ;
}
Vertex FindMinDist(MGraph Graph, int *dist, int *collected)
{
Vertex MinV, V;
int MinDist = INFINITY;
for (V = 0; V < Graph->Nv ; V++){
if (collected[V] == false && dist[V] < MinDist){
MinDist = dist[V];
MinV = V;
}
}
if (MinDist < INFINITY)
return MinV;
else
return -1;
}
bool Dijkstra(MGraph Graph, int *dist, int *path, Vertex S)
{
int collected[MaxVertexNum];
Vertex V, W;
for (V = 0; V < Graph->Nv ; V++){
dist[V] = Graph->G[S][V];
if (dist[V] < INFINITY)
path[V] = S;
else
path[V] = -1;
collected[V] = false;
}
dist[S] = 0;
collected[S] = true;
while (1){
V = FindMinDist(Graph, dist, collected);
if (V == -1)
break;
collected[V] = true;
for (W = 0; W < Graph->Nv ; W++){
if (collected[W] == false && Graph->G[V][W] < INFINITY){
if (Graph->G[V][W] < 0)
return false;
if (dist[V] + Graph->G[V][W] < dist[W]){
dist[W] = dist[V] + Graph->G[V][W];
path[W] = V;
}
}
}
}
return true;
}
void PrintPath(int *path, Vertex W, Vertex S)
{
stack<int> s;
s.push(W);
while (W != S){
W = path[W];
s.push(W) ;
}
while (!s.empty() ){
if (s.size() == 1 )
printf("%d", s.top() );
else
printf("%d ", s.top() );
s.pop() ;
}
}
int main()
{
MGraph Graph;
int VertexNum, EdgeNum;
scanf("%d %d", &VertexNum, &EdgeNum);
int *dist = (int *)malloc(sizeof (int) * VertexNum);
int *path = (int *)malloc(sizeof (int) * VertexNum);
Graph = CreateGraph(VertexNum);
Graph->Ne = EdgeNum;
Edge E = (Edge)malloc(sizeof(struct ENode ));
for(int i = 0; i < Graph->Ne ; i++){
scanf("%d %d %d", &E->V1 , &E->V2 , &E->Weight );
InsertEdge(Graph, E);
}
int S = 0;
Dijkstra(Graph, dist, path, S);
for (int i = 0; i < VertexNum; i++){
if (i != S){
PrintPath(path, i, S);
printf(" %d\n", dist[i]);
}
}
return 0;
}
- 测试数据:
- 测试结果:
Reference