摘要
最短路径问题是一个在图论研究中很经典的问题,已经被应用到GIS、GPS等信息管理系统中,为人们生活带来了很大的便利。由于在现实生活中,我们所遇到的路径问题大都不含负权,所以Dijkstra算法在解决有关最短路径问题时算是最普遍的算法之一。如果在一个图论研究中已知起点和终点求最短路径,很适合使用Dijkstra算法,以下内容我将阐述Dijkstra算法。
介绍
1.基本思想:从图的给定源点到其他各个顶点之间客观上应存在一条最短路径,在这组最短路径中,按其长度的递增次序,依次求出到不同顶点的最短路径和路径长度。即按长度递增的次序生成各顶点的最短路径,即先求出长度最小的一条最短路径,然后求出长度第二小的最短路径,以此类推,直接求出长度最长的最短路径,核心是通过递归的方式将从起始顶点到其他各顶点的最短路径全部求出。
2.为什么不能处理负权边的问题
举个例子,如果要寻找B到C之间的最短路径。使用Dijkstra算法时,比较从B->A和B->C的开销,显然B->A的更小,于是选择C的路径,并将C处理成处理过的节点,但是明明B到A到C更短。(如图1.2)
3.符号表示:V:所有结点的集合。
S:结点集合,确定最短路径结点的集合。
Q:最小优先队列。
s:源结点。
u:选择最短路径估计最小的结点。
4.算法思想说明:
设给定源点为Vs,S为已求得最短路径的终点集,开始时令S={Vs} 。当求得第一条最短路径(Vs ,Vi)后,S为{Vs,Vi} 。设下一条最短路径终点为Vj ,则Vj只有:
(1) 源点到终点有直接的弧
(2)从Vs 出发到Vj 的这条最短路径所经过的所有中间顶点必定在S中。即只有这条最短路径的最后一条弧才是从S内某个顶点连接到S外的顶点Vj 。
若定义一个数组dist[n],其每个dist[i]分量保存从Vs 出发中间只经过集合S中的顶点而到达Vi的所有路径中长度最小的路径长度值,则下一条最短路径的终点Vj必定是不在S中且值最小的顶点,即: dist[i]=Min{ dist[k]| Vk∈V-S }
5.特点:
时间复杂度为0 (N^2);
单源点最短路;
较稳定;
(4)代码较简单;
(5)负权回路不适用;
(6)可用堆优化。
二.分析
Dijkstra算法主要执行是:
数组变量的初始化:时间复杂度是O(n) ;
求最短路径的二重循环:时间复杂度是O(n2) ;
算法实现的关键:
BOOLEAN final[MAX_VEX] ;
int pre[MAX_VEX] , dist[MAX_VEX] ;
void Dijkstra_path (AdjGraph *G, int v)
{ int j, k, m, min ;
for ( j=0; j
{ pre[j]=v ; final[j]=FALSE ;
dist[j]=G->adj[v][j] ;
} dist[v]=0 ; final[v]=TRUE ;
for ( j=0; j
{ m=0 ;
while (final[m]) m++;
min=INFINITY ;
for ( k=0; k
{ if (!final[k]&&dist[m]
{ min=dist[k] ; m=k ; }
}
final[m]=TRUE ;
for ( j=0; j
{ if (!final[j]&&(dist[m]+G->adj[m][j]
{ dist[j]=dist[m]+G->adj[m][j] ;
pre[j]=m ;
}
}
}
}
算法过程示意图(图2.3)
1.初始化数D = [0, inf, 10, inf, 30, 100],
T= {v1}
2.当前离起始点v1最近的点是v3,那么可以确定v1和03的最近距离就是当前D[2]的值,并将03加入T中。
3. 既然确定了一个顶点的最短路径,下面就要根据这个新入的顶点v3的出度,发现以03的出度的有: {04} ,那么查看路径: 01- 03- v4的长度是否比01- 04短,(因为D[3]代表的就是v1- 04的长度为无穷大,而v1- V3- v4的长度为: 10+50=60),所以更新D[3]的值,得到如下结果: D= [0,inf, 10,60, 30, 100],T= {v1,v3}
4.从除v1和03外的其他值中寻找最小值,发现D[4]的值最小,同原,可以知道v1到v5的最短距离就是D[4]的值,然后,把v5加入到集合T中,考虑v5的出度是否会影响我们的数组D的值,05有 两条出度:{04, v6}可以发现: 01- 05- v4的长度为: 50, 而01- v4的值为60,所以更新D[3]的值另外,01- 05 - v6的长度为: 90,而01- 06为100,所以需要更新D[5]的值。更新结果如下:
D= [0, inf,10, 50,30,90],T= {01, 03,05}
5.继续从D中选择未确定的顶点的值中选择一个最小的值,发现D[3]的值是最小的,所以把v4加入到集合T中,考虑v4的出度,v4有一条出度: {v6} 然后可以发现:01-05- 04- v6的长度为: 60, 而v1- v6的值为90,所以更新D[5]的值,结果如下:
D = [0, inf, 10, 50, 30, 60],T= {v1, V3, V5, v4}
6同理确定了v6和v2的最短路径,得到的最终结果如下:
D= [0, inf, 10, 50,30, 60],
T= {v1, V3, U5, 0U4, V6, U2}。
三.实现
主要代码:
#include
#define INFINITY 9999
#define MAX 10
void dijikstra(int G[MAX][MAX], int n, int startnode);
int main(){
int G[MAX][MAX], i, j, n, u;
printf("\nEnter the no. of vertices:: ");
scanf("%d", &n);
printf("\nEnter the adjacency matrix::\n");
for(i=0;i < n;i++)
for(j=0;j < n;j++)
scanf("%d", &G[i][j]);
printf("\nEnter the starting node:: ");
scanf("%d", &u);
dijikstra(G,n,u);
}
void dijikstra(int G[MAX][MAX], int n, int startnode)
{
int cost[MAX][MAX], distance[MAX], pred[MAX];
int visited[MAX], count, mindistance, nextnode, i,j;
for(i=0;i < n;i++)
for(j=0;j < n;j++)
if(G[i][j]==0)
cost[i][j]=INFINITY;
else
cost[i][j]=G[i][j];
for(i=0;i< n;i++) {
distance[i]=cost[startnode][i];
pred[i]=startnode;
visited[i]=0;
}
distance[startnode]=0;
visited[startnode]=1;
count=1;
while(count < n-1) {
mindistance=INFINITY;
for(i=0;i < n;i++)
if(distance[i] < mindistance&&!visited[i]) {
mindistance=distance[i];
nextnode=i;
}
visited[nextnode]=1;
for(i=0;i < n;i++)
if(!visited[i])
if(mindistance+cost[nextnode][i] < distance[i]) {
distance[i]=mindistance+cost[nextnode][i];
pred[i]=nextnode;
}
count++;
}
for(i=0;i < n;i++)
if(i!=startnode) {
printf("\nDistance of %d = %d", i, distance[i]);
printf("\nPath = %d", i);
j=i;
do {
j=pred[j];
printf(" <-%d", j);
}
while(j!=startnode);
}
printf("\n");
}
四.总结
最短路径理论可以很好地应用到生活中,具有很重要的作用和意义,Dijkstra算法适用于边权为正的无向和有向图不适用于有负边权的图,可以将实际生活中出现的问题优化得以解决。目前,最短路径问题大多出现在道路建设方面,利用一些算法来将一些虚拟的想象和数字变成一串真实有依据的代码和数据,这些计算算法真的起到了很重要的作用。
参考文献:
1.《数据结构(c语言版)》,严蔚敏,吴伟民,清华大学出版社,P189。
2.博客园-最短路径问题(Dijkstra算法的解释)。
3.社区—Dijkstra最短路径算法。
4.社区—图之最短路径。
5.OSCHINA社区—图的最短路径。
6.慕课手记—图经典算法之-Dijkstra算法。