给定一个图G = (V, E),其中每条边的权是一个非负实数。另外给定顶点集合V中的一个顶点v,称为源
。
问题:求从源v到所有其它各个顶点的最短路径。
单源最短路径问题的贪心选择策略
:选择从源v出发,目前用最短的路径所到达的顶点
,这就是目前的局部最优解。
设置一个集合S,初始时S中仅含有源v,然后不断地用贪心选择
来扩充集合S ,即:从源v出发,选择用最短的路径所到达的顶点u,加入到集合S中,直至S包含所有V中顶点。
把从源v到顶点u
中间只经过S中顶点
的路称为从源v到顶点u
的特殊路径,用dist[u]来记录当前顶点u所对应的最短特殊路径长度
。
贪心选择:每次找到最小的dist[u],将u添加到S中,同时对数组dist[]作必要的修改。
dist[w] := min(dist[w] , dist[u]+C[u ,w])
迭代 | S | u | dist[2] | dist[3] | dist[4] | dist[5] |
---|---|---|---|---|---|---|
初始 | {1} | - - | 10 | ∞ | 30 | 100 |
1 | {1,2} | 2 | 10 | 60 | 30 | 100 |
2 | {1,2,4} | 4 | 10 | 50 | 30 | 90 |
3 | {1,2,4,3} | 3 | 10 | 50 | 30 | 60 |
4 | {1,2,4,3,5} | 5 | 10 |
50 |
30 |
60 |
prev[2]=1 prev[4]=1 prev[3]=4 prev[5]=3
由数组dis[i]可知:从顶点1到顶点2、3、4、5的最短通路的长度分别为10、50、30和60。
根据prev数组,源节点1到5的最短路径为:1,4,3,5
内层循环的时间为O(n)
。Dijkstra算法的时间复杂度为 O(n2)
却并没有给出其最短通路
。可借助prev数组得到。下面的代码是基于有向图的代码。若求无向图,在inPut()方法中打开Graph[pos2][pos1] = dis。
#include <bits/stdc++.h>
using namespace std;
const int max_ = 0x3f3f3f;
int Graph[110][110];
int dist[110]; //dist[i]存储源到顶点 i 的当前最短路长度
int visited[110]; //标记i号顶点是否已并入S
int pre[110]; //记录的是从源到顶点i的最短路径上i的前一个顶点。用来求出相应的最短路径
int m, n, p; //m代表路径的个数, n代表顶点的个数,p代表源
//回溯输出路径
void traceback(int i)
{
if(pre[i] == i)
{
printf("%d", i);
}
else
{
traceback(pre[i]);
printf(" %d", i);
}
}
void outPut()
{
for(int i = 1; i <= n; ++i)
{
if(i != p)
{
printf("点 %d 到点 %d 的最小距离为 %d\n", p, i, dist[i]);
cout << "路径为 :" << endl;
traceback(i);
cout << endl;
}
}
}
void Dijkstra()
{
//初始化
for(int i = 1; i <= n; ++i)
{
dist[i] = Graph[p][i];
visited[i] = 0;
pre[i] = p;
}
dist[p] = 0;
visited[p] = 1;
for(int i = 1; i < n; ++i)
{
int pos, min_len = max_;
for(int i = 1; i <= n; ++i)
{
if(!visited[i] && min_len > dist[i])
{
pos = i, min_len = dist[i];
}
}
visited[pos] = 1;
for(int j = 1; j <= n; ++j)
{
if(!visited[j] && dist[j] > min_len + Graph[pos][j])
{
dist[j] = min_len + Graph[pos][j];
pre[j] = pos;
}
else
continue;
}
}
}
void inPut()
{
int pos1, pos2, dis;
memset(Graph, max_, sizeof(Graph));
scanf("%d %d %d", &p, &n, &m);
// cout << "p = " << p << " n = " << n << " m = " << m << endl;
for(int i = 1; i <= m; ++i)
{
scanf("%d %d %d", &pos1, &pos2, &dis);
Graph[pos1][pos2] = dis;
//Graph[pos2][pos1] = dis;//若图是无向图,则打开此代码
}
}
int main()
{
inPut();
Dijkstra();
outPut();
}
输入
1 5 7
1 2 10
1 4 30
1 5 100
2 3 50
4 5 60
3 5 10
3 4 20
输出
点 1 到点 2 的最小距离为 10
路径为 :
1 2
点 1 到点 3 的最小距离为 50
路径为 :
1 4 3
点 1 到点 4 的最小距离为 30
路径为 :
1 4
点 1 到点 5 的最小距离为 60
路径为 :
1 4 3 5
输入
1 5 7
1 2 10
1 5 100
1 4 30
2 3 50
3 5 10
4 3 20
4 5 60
输出
点 1 到点 2 的最小距离为 10
路径为 :
1 2
点 1 到点 3 的最小距离为 50
路径为 :
1 4 3
点 1 到点 4 的最小距离为 30
路径为 :
1 4
点 1 到点 5 的最小距离为 60
路径为 :
1 4 3 5