Dijkstra算法是从求从某个源点V0到其余各顶点的最短路径问题。,其算法的具体思想是,有两个集合,一个是最短路径集合S,还有一个是未加入S的集合V-S,这些点待加入S中,只要加入了S中,那么这个点就是源点V0到这个点的最短路径了。其实这个算法的思想还是挺简单的,也是运用了贪心算法,严蔚敏书中和维基百科等,把这个问题讲的有点复杂了。我想结合严蔚敏的书中的表述和图例,尽量把这个问题讲的容易些。
1. 首先引入一个辅助向量D,它的每个分量D[i]表示当前所找到的从始点v到每个终点vi的最短路径的长度。它的初态为:若从v到vi有弧,则D[i]为弧上的权值;否则就为∞。结合上图,例如D[4]代表v0到v4的最短路径的长度,初始状态为∞,随着算法的运行,其不断更改值,最后的结果是30.
2. 假如现在求的路径的终点是vk,现在有个新加入的顶点vj,则此时最短路径只有两种情况,一种还是原来的v0到vk,还有一种情况是v0到vj再到vk。那么第二种情况的最短路径可以这样求:v0到vj的路径长度D[j]+vj到vk的弧长。这样求为了程序设计的简单。
3. 严的书中给了一个简单的证明,好好看还是有用的。假设S为最短路径的终点的集合,则可证明:下一条最短路径(设其终点为x)或者是弧(v,x),或者是中间只经过S中的顶点而最后到达顶点x的路径。
下面是书中给的算法步骤:(只是比较严谨而已,看不懂没关系,后面结合图示)
(1) 假设用带权的邻接矩阵arcs来表示带权有向图,arcs[i][j]表示弧<vi,vj>上的权值。若<vi,vj>不存在,则置arcs[i][j]为∞(在计算机上可用允许的最大值代替)。S为已找到从v出发的最短路径的终点的集合,它的初始状态为空集。那么,从v出发到图上其余各顶点vi可能到达的最短路径长度的初值为:
D[i] = arcs[Locate Vex(G,v)[i] vi∈V
(2) 选择vj,使得
D[j] = Min{D[i] | vi∈V-S}
Vj就是当前求得的一条从v出发的最短路径的终点。令
S = S ∪{j}
(3) 修改从v出发到集合V-S上任一顶点vk可大的最短路径长度。如果
D[j] + arcs[j][k] <D[k]
则修改D[k]为
D[k] = D[j] + arcs[j][k]
(4) 重复操作(2)、(3)共n-1次,即可得到结果。
下面结合书中的图讲解一下。
1.上图中第一列中,v0分别能到达的路径进行初始化,黄色标注的为最小的路径值。此时10是最小的,把v2加入最短路径集合S中。(v0默认已加入)。
2.此时的最短路径是v0,v2。这时看第二列。以v0,v2为路径,分别加入v3,v4,v5(这些点还没有进入S集合中),要是新的路径值比原来小,则更新路径长度和路径。
3.第二列的最小路径长度是30,则以v0,v4作为新的路径,把v4加入到S中去。再分别将未在S集合中的点进行测试,更新路径和路径长度。
就这样循环就可以找到最短路径了,还是挺简单的吧,嘿嘿~
下面是书中的伪代码稍加改动,使之可以运行:
#include "stdafx.h"
#include <iostream>
using namespace std;
// 假设能表示的最大值,设为无穷大的边长,不能真的设为
//最大值,要不然后面在运算的时候容易溢出
const int MAX_VALUE=100000;
const int VERTEX_NUM=6; //邻接矩阵最大的维数
//图采用邻接矩阵存储的定义
typedef struct MGraph{
int AdjMatrix[VERTEX_NUM][VERTEX_NUM]; //邻接矩阵
int vexnum; //图的顶点数量
int arcnum; //图的边数,这个程序里没有用到
};
/*****************************************************************
Name: Dijkstra
Inputs:
MGraph G - 邻接矩阵表示的图
int v0 - 表示图的起点
int P[][VERTEX_NUM] -二维数组。例如P[4][]代表v4的最短路径,这一行中,
值为1代表经过这个点,值为0代表不经过这一点,见main的输出
int D[] - 最短路径的权值
Return Value:
void
Description:
*****************************************************************/
void Dijkstra(MGraph G,int v0,int P[][VERTEX_NUM], int D[]){
//final为TRUE表示这个点已经在S中了,即已经是最短路径的终点了
int final[VERTEX_NUM]={0};
//**********************初始化v0************************
int v = 0;
for ( ; v <G.vexnum; ++v)
{
D[v] = G.AdjMatrix[v0][v];
for ( int w=0; w <G.vexnum; ++w) P[v][w]=0;
if (D[v] < MAX_VALUE)
{
P[v][v0] = true;
P[v][v] = true; //v点自身必须要在路径里面
}
}//for
D[v0] = 0; final[v0] = true; //初始化,v0属于S集合
//*******************初始化v0结束********************************
for ( int i=1; i<G.vexnum;++i) //其余的G.vexnum-1个顶点
{
int min=MAX_VALUE; //当前所知离v0顶点最近的距离
for (int w = 0; w<G.vexnum; ++w)
if (!final[w]) //w顶点在V-S中
if (D[w]<min) {v =w; min = D[w];} //w顶点离v0顶点的最近距离
final[v] = true; //离V0顶点最近的v加入S集
for (int w=0; w<G.vexnum;++w) //更新当前最短路径及距离
if(!final[w] && (min +G.AdjMatrix[v][w]<D[w])) //修改D[w]和P[w],w∈V-S
{
D[w] = min + G.AdjMatrix[v][w];
for (int j=0; j < G.vexnum;++j)
P[w][j] = P[v][j];
P[w][w] = true;
}//if
}//for
}//
int _tmain(int argc, _TCHAR* argv[])
{
//定义二维数组,一会初始化邻接矩阵
//
int arr[6][6]={100000,100000,10,100000,30,100,
100000,100000,5,100000,100000,100000,
100000,100000,100000,50,100000,100000,
100000,100000,100000,100000,100000,10,
100000,100000,100000,20,100000,60,
100000,100000,100000,100000,100000,100000};
MGraph mg;
for( int i=0; i<6; ++i )
for( int j=0; j<6; ++j )
mg.AdjMatrix[i][j]=arr[i][j]; //初始化邻接矩阵
mg.vexnum=6; //顶点的个数是6个,这里非常无耻的赋值为6
int P[VERTEX_NUM][VERTEX_NUM];
int D[VERTEX_NUM];
Dijkstra(mg,0,P,D);
for(int i=0; i<VERTEX_NUM; i++)
cout<<P[4][i]<<" ";
return 0;
}