您的位置:首页 > 其它

Dijkstra最短路径算法(SPF)

2012-12-04 22:42 204 查看
算法描述:

Dijkstra算法是一种求单源最短路算法,即从一个点开始到所有其他点的最短路。其基本原理是:每次新扩展一个距离最短的点,更新与其相邻的点的距离。当所有边权都为正时,由于不会存在一个距离更短的没扩展过的点,所以这个点的距离永远不会再被改变,因而保证了算法的正确性。不过根据这个原理,用Dijkstra求最短路的图不能有负权边,因为扩展到负权边的时候会产生更短的距离,有可能就破坏了已经更新的点距离不会改变的性质。

如果用本算法求一个图中全部的最短路,则要以每个点为源调用一次Dijkstra算法。

使用条件与限制:

有向图无向图都可以使用本算法,无向图中的每条边可以看成相反的两条边。

用来求最短路的图中不能存在负权边。(可以利用拓扑排序检测)

算法流程:

在以下说明中,s为源,w[u,v]为点u和v之间的边的长度,结果保存在dist[]

初始化:源的距离dist[s]设为0,其他的点距离设为无穷大,同时把所有的点的状态设为没有扩展过。
循环n-1次:

在没有扩展过的点中取一距离最小的点u,并将其状态设为已扩展。
对于每个与u相邻的点v,执行Relax(u,v),也就是说,如果dist[u]+w[u,v]<dist[v],那么把dist[v]更新成更短的距离dist[u]+w[u,v]。此时到点v的最短路径上,前一个节点即为u。

结束。此时对于任意的u,dist[u]就是s到u的距离。

复杂度分析:

最简单的实现方法就是,在每次循环中,再用一个循环找距离最短的点,然后用任意的方法更新与其相邻的边,时间复杂度显然为


对于空间复杂度:如果只要求出距离,只要n的附加空间保存距离就可以了(距离小于当前距离的是已访问的节点,对于距离相等的情况可以比较编号或是特殊处理下)。如果要求出路径则需要另外V的空间保存前一个节点,总共需要2n的空间。

下面具体的代码实现:
#pragma once
#include<iostream>
#include<fstream>
#include"F:\QQPCmgr\documents\visual studio 2010\Projects\Graph\Graph_matrix\GraphAdjMatrix.h"
using namespace std;

template<class T, class E>
void DijkstraAlgorithm(GraphAdjMatrix<T,E> &targetG, const T &beginVertex, E dist[], int path[])
{
int verticesNum = targetG.GetVerticesNum(); //获取图顶点数目
int beginIdx = targetG.GetVerticesPos(beginVertex); //获取源顶点索引值(在这里都是用顶点索引值代表顶点的)
bool *state = new bool[verticesNum]; //一个状态值数组,用来表示已求得的最短路径顶点集
E weigth_temp, minWeight; //辅助求最短路径变量
E maxWeightFlag = targetG.GetMaxWeight(); //获取图中约定好的代表顶点之间不可达的权值标示

for(int i = 0; i < verticesNum; i++) //循环用来做初始化信息
{
dist[i] = targetG.GetWeight(beginIdx, i); //dist[]数组中最初存放源点能直接到达的点的边权值(这个以后会根据算法的进行慢慢更新的)
state[i] = false; //把所有的状态值都置为false 表示都没有求出最短路径
if((i != beginIdx) && (dist[i] != maxWeightFlag)) //不等于源点并且源点与顶点i之间有边
{
path[i] = beginIdx; //pre = path[i]表示:能最近到达i顶点的上一步是pre(算法只着眼于能到达本顶点路径长度最短的上一个顶点,通过回溯可以一步一步的回溯到源点)
}
else
{
path[i] = -1; //表示:到达顶点i路径长度最短的上一个顶点不存在,意味着不存在源点到达顶点i最短路径
}
}

state[beginIdx] = true; //算法起始:将源点放入已求得最短路径顶点集合中
dist[beginIdx] = 0; //从源点到达源点的边权值为0

for(int count = 0; count < verticesNum-1; count++) //总共循环图定点数目-1次
{
minWeight = maxWeightFlag;
int nextIdx; //此索引记录:当前dist[]中值最小的那一个(通过循环实现)
for(int i = 0; i < verticesNum; i++)
{
if((state[i] == false) && (dist[i] < minWeight))
{
minWeight = dist[i];
nextIdx = i;
}
}

state[nextIdx] = true; //可以认为:就目前来看源点到达nextIdx的路径是最短的,将其放入状态数组中
//站在nextIdx顶点的角度来看,是否能借助于我,使得源点到达其他顶点的路径会更短呢?下面是搜索与判断,更新
for(int k = 0; k < verticesNum; k++)
{
weigth_temp = targetG.GetWeight(nextIdx, k);
//如果确实通过nextIdx顶点使得源点到达顶点k的路径更小了,执行下面if语句
if((state[k] == false)&&(weigth_temp != maxWeightFlag)&&(dist[nextIdx] + weigth_temp < dist[k]))
{
dist[k] = dist[nextIdx] + weigth_temp;
path[k] = nextIdx; //这时要修改能够最短到达顶点k的上一个顶点
}
}

}//外层大循环

}

template<class T, class E>
void PrintShortestPath(GraphAdjMatrix<T,E> &targetG, const T &beginVertex, E dist[], int path[])
{
int begVtxNum = targetG.GetVerticesPos(beginVertex); //获取源点索引
int verticesNum = targetG.GetVerticesNum();
int temp, count;
int *tempDist = new int[verticesNum]; //用来存储源点到达其他的任意一个顶点的路径信息(存储的仍然是顶点索引)

for(int vtxnum = 0; vtxnum < verticesNum; vtxnum++) //搜索所有的顶点
{
if(vtxnum != begVtxNum) //不能对源点输出最短路径
{
temp = vtxnum;
count = 0;

while(temp != begVtxNum)
{
tempDist[count++] = temp; //先把目的顶点vtxnum的索引放入辅助数组tempDist[]中
temp = path[temp]; //回溯找到源点
}
cout<<"顶点:<"<<targetG.GetVertex(vtxnum)<<">的最短路径:"<<beginVertex<<" ";
while(count > 0)
{
cout<<targetG.GetVertex(tempDist[--count])<<" "; //对辅助数组进行逆向输出
}
cout<<"最短路径长度为:"<<dist[vtxnum]<<endl;
}
}

delete []tempDist;
}


下面是运行截图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: