其实这个算法写了很多次了,但是总是理解不深。这次专门拿出时间好好的分析一下。

【基本原理】

称为三角算法。以下面的简单图示表示。假设v1是我们的源点,它的最短路径长度已固定(为0)。那么首先找到的最短路径是v1-v2。找到v2之后,我们最短路径长度已固定的节点的集合就变成了{v1,v2}。那么需要更新与v2邻接的所有节点的最短路径长度的值,由于1+2<5,所以v3的最短路径长度就更新为3而不是保持为5。当然,如果v2到v3的距离大于4,v3的最短路径长度是不需要更新的。


Dijkstra的最短路径算法往往有几种存储方式,例如:

(1)邻接矩阵。形如a[i][j]来存储节点i与节点j之间的路径。

(2)邻接表。单是这一种存储方式就有两种变化:a)使用STL的vector,以节点为存储单位。每一个节点拥有一个node对象的vector,然后每一个node对象中存储下一个节点及到此节点的边长。虽然这种方法看起来很像上面的邻接矩阵表示法,但是由于vector的初始化比较灵活,不需要N^2的空间,因此实质上还是符合邻接表的初衷:在稀疏图中节省空间。b)以边为单位存储。每个边的对象包括起始节点、目的节点以及此边边长。另外还有将边存储为链表的,每个边的对象包括指向下条边的指针。

不光存储方式有变化,具体实现也有很多tricks。比如:在所有的边中选取距离当前集合中的节点最近的一条边时,可以采用堆来减少时间复杂度,无需遍历整个剩余边的集合。这样降低了整个程序的复杂度。

甚至输出方式也不止一种,有的要求输出源点到其他节点的最短路径,有的要求更高:要保存路径上的每一个节点。

【代码】

 

这里仅仅给出一个最原始的版本:使用邻接表存储,不使用堆,仅输出源点到其他节点的最短路径长度的值,而不输出整个路径。

 

using namespace std;

const int INF = 0x7FFFFFFF
;

typedef
struct
node{
    
int to;//以当前node为起点的边的终点

    int length;//此边的边长
     node(int n = -1, int l = INF ) { to = n ; length = l ; }
};
typedef vector
<node> vnode;//
每一个顶点的出边数组

//寻找最短路径尚未固定,且当前路径最小的那个节点。

static int nearestNotfixed( vector<int> &shortest, vector<bool> &fixed, int num ){
    
int nearestVertex = -1
;
    
int nearestDist =
INF;

    
for( int i = 0 ; i < num ; ++
i )
        
if( ! fixed
[i] )
            
//找到最短路径最小的那个节点

            if( shortest[i] < nearestDist )
             {
                 nearestDist
=
shortest[i];
                 nearestVertex
=
i;
             }
    
return
nearestVertex;
}

/*
每当寻找到新的最短路径固定的节点之后,更新与它邻接的所有节点的最短路径。
@adjacent: 与刚找到的节点邻接的所有节点
@nearest : 最短路径已固定的节点到刚找到的节点之间的路径长度
*/

static void refreshDist(vector<node> &adjacent, vector<int> &shortest, vector<bool> &fixed, int nearestDist ){
     vector
<node>::iterator iter =
adjacent.begin();
    
for( ; iter != adjacent.end() ; ++
iter)
        
if( ! fixed[iter->
to] )
            
if( iter->length + nearestDist < shortest[iter->
to] )
                 shortest[iter
->to] = iter->length +
nearestDist;
}

//main function

void dijkstra( vector<vnode> &graph, vector<int> &shortest, vector<bool> &fixed, int num, int source ){
     shortest[source]
= 0
;
    
for
( ; ; ){
        
//寻找一个最短路径尚未固定的节点

        int nearest = nearestNotfixed( shortest, fixed, num);
        
//所有节点的最短路径都已经固定,因此找不到可以更新的节点,程序退出。

        if( nearest == -1 )
            
break
;
        
fixed[nearest] = true
;
        
//更新邻接于刚找到的节点的所有节点的最短路径

         refreshDist( graph[nearest], shortest, fixed, shortest[nearest] );
     }
}

int
main(){
    
/////////////////initialization//////////////////////

    int N;//total number of vertices
    int source;//the source vertex

    
/*
     输入文件格式:第一行为总顶点数 第二行为源点序号
     以下每行为一条边:起点 终点 边长度
    
*/
     ifstream infile(
"dijkstra.txt");
     infile
>>
N;
     infile
>>
source;

    
//graph[i]是第i个节点的邻接节点的数组。

     vector<vnode> graph(N);
    
//距离源点的最短路径。

     vector<int> shortest(N,INF);
    
//最短路径已固定(不会再被更新)的节点的集合,固定后标记为true。

     vector<bool> fixed(N,false);

    
//
input loop
    
//按照数据格式输入每条线段的起点、终点和长度

    int from,to,length;
    
while( !
infile.eof() ){
         infile
>>from>>to>>
length;
         graph[from].push_back( node(to,length) );
     }
    
/////////////////////end of initialization
//////////////////
     dijkstra( graph, shortest, fixed
, N, source );
    
//打印最短路径数组。其中源点对应的项肯定为0。

     printVector(shortest);
    
return 0
;
}

【输入数据】

输入的dijkstra.txt文件如下(这个例子实际上是《数据结构与算法分析C++描述》中文第三版中p.258的例子),但因为例子中的节点都是从1开始,输入的时候需要改为从0开始:

7
0
0 1 2
0 3 1
1 3 3
1 4 10
2 0 4
2 5 5
3 2 2
3 4 2
3 5 8
3 6 4
4 6 6
6 5 1

【示意图】

这个例子的图示如下:


posted on 2011-05-12 01:24  微型葡萄  阅读(513)  评论(0编辑  收藏  举报