Dijkstra算法
2015-10-24 15:52
363 查看
Dijkstra算法用于求解单源最短路径问题。
Dijkstra算法最核心的步骤在于,每次从剩余节点中选取一个节点v加入已访问节点集合的时候,我们便以v为中间节点,查看从源点出发经过v到剩余节点k和不经过v到剩余节点k哪个更短,如果经过v到剩余节点k更短的话,我们需要更新从源点到k的距离值,以及将k的前驱设置为v。
换句话说就是,当我们从源点向目标节点走的时候,每走一步,下一步面临若干选择,当我们选择了目前的一条最短路,即选择了某个k点的时候,从源点到目标点的最短路就多了一种选择——走k和不走k。如果我们走k到剩余节点更短的话,我们当然要选择k,并且要更新这个最短路径。
我们需要三个数组
dist数组,记录从源点到某点的最短路径长度
path数组,记录v点最短路径的上一个节点
visit数组,记录某点有没有被访问过
(1)开始,我们站在源点,加入是0点,我们只能看到1、2、3,所以
开始时,我们选择了最小值1节点,此时剩余节点就是2、3、4、5、6。
以1为中间节点,更新到剩余顶点的值。
这里解释一下,我们加入了1号节点,我们站在1,下一步我们能看到2和4,所以,2和4到源点的最短路可能因为经过1这条最新的路径而改变,例如,本来从源点到2的距离为6,由于经过1,源点到2的距离dist[1]+1=5<6,所以,源点到2的最短距离我们就要更新为5,且这个距离是通过走1这个节点得到的,所以,2的最短路径的前驱就更新为1。同理,0到4本来是无穷(因为站在0点,不知道怎么才能到4),但是,当我们站在1点,到4的距离为dist[1]+7=11,小于无穷,所以,我们更新源点到4的最短距离为11,到4的最短路径要经过1,所以4的前驱设置为1
(2)在当前的dist数组中选择最小的值2,下一步就是要以2为中间节点进行观察,也就是说,我们现在站在2处。此时剩余节点是3、4、5、6。
同理,站在2处,我们能看到4和5,对于4,经过2到4距离为dist[2]+6=11,不更新。经过2到5,距离为dist[2]+4=9,小于无穷,更新之。
后面的操作就是进行重复,直到所有点的visit值为1。
最后我们得到的结果为
最后dist数组就记录了从源点0到每个点的最短路径长度,path数组记录了最短路径上该点的上一个节点。
最后我们要输出最短路径,就是要不断利用path数组,借助栈进行输出。
例如,我们要输出从0到6的最短路径,6进栈,查path表,6的前面是4,4进栈,4的前面是5,5进栈,5的前面是2,2进栈,2的前面是1,1进栈,1的前面是0,0进栈,0的前面是-1,结束,所有元素出栈就是最短路径:0->1->2->5->6,最短路径长度为16。
算法的时间复杂度为O(n2)。
Dijkstra算法最核心的步骤在于,每次从剩余节点中选取一个节点v加入已访问节点集合的时候,我们便以v为中间节点,查看从源点出发经过v到剩余节点k和不经过v到剩余节点k哪个更短,如果经过v到剩余节点k更短的话,我们需要更新从源点到k的距离值,以及将k的前驱设置为v。
换句话说就是,当我们从源点向目标节点走的时候,每走一步,下一步面临若干选择,当我们选择了目前的一条最短路,即选择了某个k点的时候,从源点到目标点的最短路就多了一种选择——走k和不走k。如果我们走k到剩余节点更短的话,我们当然要选择k,并且要更新这个最短路径。
举个栗子
我们需要三个数组
dist数组,记录从源点到某点的最短路径长度
path数组,记录v点最短路径的上一个节点
visit数组,记录某点有没有被访问过
(1)开始,我们站在源点,加入是0点,我们只能看到1、2、3,所以
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
dist | inf | 4 | 6 | 6 | inf | inf | inf |
path | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
visit | 1 | -1 | -1 | -1 | -1 | -1 | -1 |
以1为中间节点,更新到剩余顶点的值。
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
dist | inf | 4 | 5 | 6 | 11 | inf | inf |
path | -1 | 0 | 1 | 0 | 1 | -1 | -1 |
visit | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
(2)在当前的dist数组中选择最小的值2,下一步就是要以2为中间节点进行观察,也就是说,我们现在站在2处。此时剩余节点是3、4、5、6。
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
dist | inf | 4 | 5 | 6 | 11 | 9 | inf |
path | -1 | 0 | 1 | 0 | 1 | 2 | -1 |
visit | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
后面的操作就是进行重复,直到所有点的visit值为1。
最后我们得到的结果为
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
dist | inf | 4 | 5 | 6 | 10 | 9 | 16 |
path | -1 | 0 | 1 | 0 | 5 | 2 | 4 |
visit | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
最后我们要输出最短路径,就是要不断利用path数组,借助栈进行输出。
例如,我们要输出从0到6的最短路径,6进栈,查path表,6的前面是4,4进栈,4的前面是5,5进栈,5的前面是2,2进栈,2的前面是1,1进栈,1的前面是0,0进栈,0的前面是-1,结束,所有元素出栈就是最短路径:0->1->2->5->6,最短路径长度为16。
#include<cstdio> #define MAXSIZE 100 #define INF 99999 int MGraph[MAXSIZE][MAXSIZE]; //图 int path[MAXSIZE]; int dist[MAXSIZE]; int visit[MAXSIZE]; void init(int n) { for(int i = 0; i < n; ++i) { path[i]=-1; visit[i]=0; } } void createG(int e) { int val; int a,b; for(int i = 0; i < e; ++i) { for(int j = 0; j < e; ++j) { MGraph[i][j] = INF; } } for(int i = 0; i < e; ++i) { scanf("%d%d%d",&a,&b,&val); MGraph[a][b]=val; } } //获取当前从原点到剩余顶点最短路径坐标 int getlowest(int n) { int mm=9999; int u; for(int i=0; i<n; ++i) { if(visit[i]==0&&dist[i]<mm) { u=i; mm=dist[i]; } } return u; } void dijkstra(int v0,int n) { for(int i=0; i<n; ++i) { dist[i]=MGraph[v0][i]; if(MGraph[v0][i]!=INF) path[i]=v0; else dist[i]=INF; } visit[v0]=1; for(int i=0;i<n;++i) { int u=getlowest(n); visit[u]=1; for(int j=0;j<n;++j) { if(visit[j]==0&&dist[u]+MGraph[u][j]<dist[j]) { dist[j]=dist[u]+MGraph[u][j]; path[j]=u; } } } } //利用栈打印最短路径,d为目标点 void printPath(int n,int d) { int s[MAXSIZE]; int top=0; int c=d; while(path[d]!=-1) { s[top]=path[d]; d=path[d]; ++top; } for(int i=top-1;i>=0;--i) { if(i!=0) printf("%d -> ",s[i]); else printf(" %d -> %d\n",s[i],c); } printf("最短路径长度:%d",dist[c]); } int main() { int n,e; scanf("%d%d",&n,&e); init(n); createG(e); int v0; scanf("%d",&v0); dijkstra(v0,n); printPath(n,6); return 0; }
算法的时间复杂度为O(n2)。
相关文章推荐
- 乱侃OOD
- bzoj3943【Usaco2015 Feb】SuperBull
- 使用javaScript动态加载Js文件和Css文件
- Linux netstat
- WebView后台耗电问题
- 怎样做一个iOS App的启动分层引导动画
- 新手Kinect学习记录(一).——工作原理
- 何为js的语句声明
- 扩展方法
- Android support.v7库中的几种控件,布局控件
- 一看就会Android之View类和ViewGroup的关系
- android中影藏状态栏和标题栏的几种方法
- [No00002F]3步,教你如何分解需求
- 变形--矩阵 matrix()
- 模块解耦
- 复杂系统的五个属性
- corosync+pacemaker+nfs+crmsh配置web服务的高可用
- 每天一个linux命令(15):rmdir 命令
- android 修改RadioButton的drawTop图片大小
- 有关信息ACM/ICPC竞争环境GCC/G++叠插件研究记录的扩展