Dijkstra算法
一.最短路径的最优子结构性质
该性质描述为:如果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算法
由上述性质可知,如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点。那么(Vi...Vk)也必定是从i到k的最短 路径。为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最 短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短距离dist[j]=min{dist[j],dist[i]+matrix[i][j]}。根 据这种思路,
假设存在G=<V,E>,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。
1.从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;
2.更新与i直接相邻顶点的dist值。(dist[j]=min{dist[j],dist[i]+matrix[i][j]})
3.知道U=V,停止。
三.算法实例
先给出一个无向图
用Dijkstra算法找出以A为起点的单源最短路径步骤如下
代码:
#include <iostream> #include <cstring> using namespace std; #define DEBUG 0 #define INF 0x7fffffff #define MAX 20 int N, V; int graph[MAX][MAX]; int dist[MAX]; int prev[MAX]; bool visited[MAX]; void PrintPath() { for( int i = 1; i <= V; i++ ) { cout << "1 --> " << i << " : " << dist[i] << endl; } for( int i = 1; i <= V; i++ ) //计算从1到每个顶点的距离 { int path[MAX] = {0}; int step = 0; int cur = i; do { path[step++] = cur; cur = prev[cur]; } while( cur != -1 ); //前一个点为-1,则结束 for( int j = step - 1; j >= 0; j-- ) { cout << path[j] << " "; } cout << endl; } } int GetMinDist() { int index, min = INF; for( int i = 1; i <= V; i++ ) { if( !visited[i] && min > dist[i] ) { min = dist[i]; index = i; } } return index; } void Dijkstra( int v ) { for( int i = 1; i <= V; i++ ) { if( graph[v][i] == INF ) { dist[i] = INF; prev[i] = -1; } else { dist[i] = graph[v][i]; prev[i] = v; } visited[i] = false; } dist[v] = 0; visited[v] = true; for( int i = 1; i < V; i++ ) //迭代V-1次,不用计算源点了,还剩下V-1个需要计算的顶点 { int u = GetMinDist(); visited[u] = true; for( int j = 1; j <= V; j++ ) //更新dist数组 { if( visited[j] == false && graph[u][j] != INF && dist[u] != INF && dist[j] > dist[u] + graph[u][j] ) { dist[j] = dist[u] + graph[u][j]; prev[j] = u; } } } } void InitData() { memset( visited, false, sizeof( visited ) ); //初始化visited for( int i = 0; i <= V; i++ ) { for( int j = 0; j <= V; j++ ) { graph[i][j] = INF; } dist[i] = INF; } } int main() { int a, b, value; while( cin >> V, V ) // 输入顶点数 { cin >> N; //输入边数 InitData(); for( int i = 1; i <= N; i++ ) { cin >> a >> b >> value; graph[a][b] = graph[b][a] = value; } Dijkstra( 1 ); PrintPath(); } }
输入文件:
/* 6 10 1 2 4 1 3 8 2 3 3 2 4 4 2 5 6 3 4 2 3 5 2 4 5 4 4 6 9 5 6 4 result : 1 --> 1 : 0 1 --> 2 : 4 1 --> 3 : 7 1 --> 4 : 8 1 --> 5 : 9 1 --> 6 : 13 1 1 2 1 2 3 1 2 4 1 2 3 5 1 2 3 5 6 */
Floyd算法
1.算法思想原理:
Floyd算法是一个经典的动态规划算法。从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。所以,假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,则设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
2.算法描述:
a.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
b.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。
3.Floyd算法过程矩阵的计算----十字交叉法
方法:两条线,从左上角开始计算一直到右下角 如下所示
给出矩阵,其中矩阵A是邻接矩阵,而矩阵Path记录u,v两点之间最短路径所必须经过的点
相应计算方法如下:
最后A3即为所求结果.
算法实现:
#include <iostream> using namespace std; #define INF 65536 #define MAX 20 int graph[MAX][MAX]; int KeyPoint[MAX][MAX]; int V, E; void PrintPath() { cout << graph[1][V] << endl; int path[MAX]; int step = 0; int cur = V; while(cur != 0) { path[step++] = cur; cur = KeyPoint[1][cur]; } path[step++] = 1; //保存起点 for (int j = step - 1; j >= 0; j--) { cout << path[j] << " "; } cout << endl; } void Floyd() { graph[1][1] = 0; for(int k = 1; k <= V; k++) //对于每一个节点k,检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立 for(int i = 1; i <= V; i++) for(int j = 1; j <= V; j++) if(graph[i][j] > graph[i][k] + graph[k][j]) { graph[i][j] = graph[i][k] + graph[k][j]; KeyPoint[i][j] = k; } PrintPath(); } void InitData() { for(int i = 1; i <= V; i++) { for(int j = 1; j <= V; j++) { graph[i][j] = INF; KeyPoint[i][j] = 0; } } } int main() { int a, b, length; while(cin >> V, V) //输入顶点数 { InitData(); cin >> E; //输入边数 for(int i = 1; i <= E; i++) { cin >> a >> b >> length; graph[a][b] = graph[b][a] = length; } Floyd(); } }
测试用例
/* 6 10 1 2 4 1 3 8 2 3 3 2 4 4 2 5 6 3 4 2 3 5 2 4 5 4 4 6 9 5 6 4 result: 13 1 2 3 5 6 */