参考《计算机算法设计与分析(第二版)》王晓东电子工业出版社
大二时地理信息系统实习写过Dijkstra寻找最短路径程序,当时对计算机编程还不熟悉,很多知识都不了解,尤其是算法等,今天重新编了一遍,也较为清楚了。以下是自己零星所想,一些思维片段,或许不容易理解。编程以蔽之。
Dijkstra算法思想:
顶点集合G=(V,E)
已知w[i][j]为边(i,j)的权。当(i,j)∉E时,w[i][j]=INT_MAX最大数,视为无边路径连接
dist[i]表示当前从源到顶点i的最短特殊路径长度。
带权有向图G=(V,E),V={1,2,。。。},顶点int start是源
输入为
int n点个数
int start源
w[i][j]为边(i,j)的权。当(i,j)∉E时,w[i][j]=INT_MAX最大数,视为无边路径连接
输出:
Type dist[i]表示当前从源到顶点i的最短特殊路径长度。
int prev[i]表示从start到i点的最短路径中,i点的前一个点。初始第一步:
集合S(贪心集)初始没有元素。
bool s[n] ;
for all s[i];
s[i]=false;s中没有任何元素
for(int i=1;i<=n;i++)
{
dist[i]=w[start][i];//起点到第i点的距离
if(dist[i]==INT_MAX)//起点到i点无边连接
prev[i]=0;//从start到i点的最短路径中,i点的前一个点为0,也就是没有边连接
else prev[i]=start;// start和i点直接相连,也就是说从start到i点的最短路径中,i点的前一个点为start
}
初始化求得start点到各点的直接距离。
置dist[start]=0; //start到自身距离为0
s[start]=true;//此时已经求得经过start点到各点的最短距离,所以将源start加到贪心集s中。
//剩余n-1个点,求经过u点到其余各点的最短距离,直至所有点多加入到s中,此时结束
for(int i=1;i<n;i++)//第i次循环,每次循环加入特殊最短路径
{
int temp=INT_MAX;
int u=start;//源
for(int j=1;j<=n;j++)
{
if((!s[j]) && (dist[j] <temp))
{
u=j;//对每一个点,查看是否在s中,如果还未加入s,并且小于temp
temp = dist[j];
}
}//得到联通start到j(j∈V-S)最短特殊路径的u点(u∈V-S)
//将u加入s
s[u]=true;
for(int j=1;j<=n;j++)
{
if((!s[j]) && (w[u][j] <INT_MAX))
{
Type newdist=dist[u]+w[u][j];
if(newdist < dist[j])
{
dist[j]=newdist;
prev[j]=u;
}
}
}
}
也就是说先假设源start点到各点的连接距离为最短距离,然后判断新加入一个点u,如果通过u点能使连接各点的距离缩短,就将start点到i点的距离更新,且记i点的前一节点为u。把u点加入到s集合中,也就是说,经过s中的点到各点的最短距离和相应路径已经知道,从剩余的V-S中取出联通距离最短的一点u加入到结合S,并判断u是否能使目前start到V-S中最短距离缩短,如果能则修改最短距离dist[i]和V-S各点的前一节点为u。直至所有节点都加入到S,dist就记录了从源到所有其他顶点之间的最短路径长度。
VC++下编程实现
// Dijkstra.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "vector"
const double INFINITE=99999999;
/************************************************************************************************/
/* 输入为 Dijkstral单源最短路径
* int n 点个数
* int start 源
* weight[i][j] 边(i,j)的权。当(i,j)不属于E时,w[i][j]=INFINITE最大数,视为无联通
*
* 输出:
* Type dist[i] 表示当前从源到顶点i的最短特殊路径长度。
* int prev[i] 表示从start到i点的最短路径中,i点的前一个点。
* 作者:Archie
* 日期:2012年9.2日
*************************************************************************************************/
void Dijkstral(int n,int start,double *w,double dist[],int prev[]);
double weight[]={0.0 ,10.0 , INFINITE , 30 ,100 ,
INFINITE ,0 , 50 , INFINITE ,INFINITE ,
INFINITE ,INFINITE , 0 , INFINITE ,10 ,
INFINITE ,INFINITE , 20 , 0 ,60 ,
INFINITE ,INFINITE , INFINITE , INFINITE ,0 };
int main(int argc, char* argv[])
{
int n=5;
int start=1;
double dist[5];
int prev[5];
Dijkstral(n, start, weight, dist, prev);
for (int i=0;i<n; i++)
{
std::vector<int> path;
path.push_back(i);
int t=prev[i];
while (t!=-1 && t!=start-1)
{
path.push_back(t);
t=prev[t];
}
path.push_back(start-1);
printf("第%d个点到第%d个点的最短距离为%f\n",start,i+1,dist[i]);
printf("其路径为:\n");
for (std::vector<int>::reverse_iterator r_iter=path.rbegin();r_iter!=(path.rend()-1);r_iter++)
printf("%d—>",(int)*r_iter+1);
printf("%d\n",i+1);
}
return 0;
}
void Dijkstral(int n,int start,double *w,double dist[],int prev[])
{
bool *s=new bool[n];
for(int i=1;i<=n;i++)
{
s[i-1]=false; //初始集合S(贪心集)没有元素
dist[i-1]=*(w+(start-1)*n+(i-1)); //起点start到第i点的距离 传进来的数组须连续存放即数组形式a[m][n]
if(dist[i-1]==INFINITE) //起点到i点无边连接
prev[i-1]=-1; //从start到i点的最短路径中,i点的前一个点为-1,也就是没有边连接
else prev[i-1]=start-1; //start和i点直接相连,也就是说从start到i点的最短路径中,i点的前一个点为start
}
// 初始化求得start点到各点的直接距离。
dist[start-1]=0; //start到自身距离为0
s[start-1]=true; //此时已经求得经过start点到各点的最短距离,所以将源start加到贪心集s中。
//剩余n-1个点,循环第i个点u加入s,直至所有点加入到s中
for(i=0;i<n-1;i++)
{
double temp=INFINITE;
int u=start-1;//源
for(int j=0;j<n;j++)
{
if((!s[j]) && (dist[j] <temp))
{
u=j;//对每一个点,查看是否在s中,如果还未加入s,并且小于temp
temp = dist[j];
}
}//得到联通start到j(j∈V-S)最短特殊路径的u点(u∈V-S)
//将u加入s
s[u]=true;
for(j=0;j<n;j++)
{
if((!s[j]) && (*(w+u*n+j) <INFINITE))
{
double newdist=dist[u]+*(w+u*n+j);
if(newdist < dist[j])
{
dist[j]=newdist;
prev[j]=u;
}
}
}
}
if (s!=NULL)
{
delete[]s;
s=NULL;
}
}
针对上图结果