Dijkstra最短路径问题求解

来源:互联网 发布:淘宝发货前申请了退款 编辑:程序博客网 时间:2024/04/29 17:04

Dijkstra(迪科斯彻)无负权值最短路径问题。此方法可以计算有向图,无向图中任意两结点间最短路径,但路径的权不能为负。

Dijkstra最短路径算法又分顺向和逆向。比如计算某图中结点a到结点b的最短路径,顺向Dijkstra算法是从起点结点a开始延伸,一直到终点结点b;而逆向Dijkstra算法是从终点结点b开始延伸,一直到起点结点a。本文只介绍顺向Dijkstra算法,也是最常用的。

首先定义几个概念,方便后续的描述:

如果选择从结点i向其所有相邻可达的结点延伸,则称结点i为延伸点;没有充当过延伸点的结点称为可变标记点,充当过延伸点的结点称为固定标记点。延伸点只从可变标记点中选取。

假设取结点1为源点,用(i,kj)来标记结点j,其中i表示是从结点i延伸到j,kj表示从源点1到结点j的路径长度,且此时j为可变标记点。若结点j是固定标记点,则使用[i,kj]来标记。
假设图中有n个结点,结点1为源点,结点n为汇点,计算从结点1到结点n的最短路径。
Dijkstra算法步骤:
1、给图中结点j(j=1,2,3,...,n)一个可变标记(1,kj),1表示从结点1延伸到j,kj表示路径长度,j=1时,kj=0,j=2,3,...,n时,kj=∞。
2、选择所有可变标记点中kj最小的结点x。若结点j不存在,则运算结束;否则转到3。
3、对于结点x相邻可达的每一个非固定标记点y,若kx+dxy<ky,则把结点y的标记修改为(x,kx+dxy);否则不修改。
4、将结点x的标记修改成固定标记,转到2。

如图1,计算结点1到结点7的最短路径长度及走向。

首先加上初始可变标记,如图2。

所有可变标记中,结点1标记的长度最小为0,所以从结点1向其相邻可达的可变标记点2和3延伸,延伸到结点2的距离为0+5=5<∞,则修改结点2的标记为(1,5),延伸到结点3的距离为0+9=9<∞,则修改结点3的标记为(1,9),修改结点1的标记为固定标记[1,0],如图3。

此时,所有可变标记中,结点2标记的长度最小为5,所以从结点2向其相邻可达的可变标记5延伸,延伸到结点5的距离为5+12=17<∞,修改结点5的标记为(2,17),修改结点2的标记为固定标记[1,5],如图4。

此时,所有可变标记中,结点3标记的长度最小为9,所以从结点3向其相邻可达的可变标记4和6延伸,延伸到结点4的距离为9+15=24<∞,修改结点4的标记为(3,24),延伸到结点6的距离为9+23=32<∞,修改结点6的标记为(3,32),修改结点3的标记为固定标记[1,9],如图5。

此时,所有可变标记中,结点5标记的长度最小为17,所以从结点5向其相邻可达的可变标记4和7延伸,延伸到结点4的距离为17+5=22<24,修改结点4的标记为(5,22),延伸到结点7的距离为17+14=31<∞,修改结点7的标记为(5,31),修改结点5的标记为固定标记[2,17],如图6。

此时,所有可变标记中,结点4标记的长度最小为22,所以从结点4向其相邻可达的可变标记6和7延伸,延伸到结点6的距离为22+8=30<32,修改结点6的标记为(4,30),延伸到结点7的距离为22+7=29<31,修改结点7的标记为(4,29),修改结点4的标记为固定标记[5,22],如图7。

此时,所有可变标记中,结点7标记的长度最小为29,但结点7无相邻可达的可变标记点,修改结点7的标记为固定标记[4,29]。此时,结点6是唯一的可变标记,修改为固定标记[4,30]。至此,运算结束,最终结果如图8。

得到结点1到结点7的最短路径长度为29,倒退找出路径走向为:7<=4<=5<=2<=1。同时也得到了结点1到其他各结点的最短路径:6<=4<=5<=2<=1,长度为30;5<=2<=1,长度为17;4<=5<=2<=1,长度为22;3<=1,长度为9;2<=1,长度为5。
在此算法中,如果计算结点1到结点4的最短路径,一旦结点4成为固定标记点,即可结束运算,得到最短路径长度及走向,以提高计算效率。有了这样一个思路,代码就easy了。

代码的思路与算法步骤类似,需要输入结点数N和边信息矩阵d,且d[i][j]中有规定,若结点i,j直接可达,则d[i][j]为权值;若i等于j,则d[i][j]为0;若i,j不直接可达,则d[i][j]为无穷大(可以定义一个足够大数,充当无穷大,但最好不要使用int型数据所能表示的最大数)。

为编程方便,结点的下标从0开始,本代码只是针对本例题的,即路径的起点结点是1,提供本代码旨在抛砖引玉,在本代码的基础上稍作修改,即可实现计算任何两结点之间的最短路径。

#include<iostream>using namespace std;#define H 1000000class NODEtag//标记类{public:NODEtag():tag(0){}int node;//延伸结点int length;//长度int tag;//tag == 0表示可变标记,tag == 1表示固定标记};int getminlength(NODEtag *nodetag,int n)//寻找可变标记中长度最小的结点{int i,len = H,t = -1;for(i=0;i<n;i++){if(nodetag[i].tag == 0&&nodetag[i].length < len){len = nodetag[i].length;t = i;}}return t;}void shortpath(int **d,int N,NODEtag *nodetag)//计算最短路径{int i,t,n = N;while(n--){t = getminlength(nodetag,N);//获取当前可变标记结点中,长度最短的结点for(i=0;i<N;i++){if(d[t][i] != -1&&nodetag[i].tag == 0&&t != i)//相邻可达的可变标记点{if(nodetag[t].length + d[t][i] < nodetag[i].length){nodetag[i].node = t;nodetag[i].length = nodetag[t].length + d[t][i];}}}nodetag[t].tag = 1;}}void getpath(NODEtag *nodetag,int n)//获取路径走向,从终点开始以倒退方式找出路径{cout<<n+1;while(n!=0){n = nodetag[n].node;cout<<" <= "<<n+1;}cout<<endl;}int main(){int **d,N,i,j;NODEtag *nodetag;cout<<"输入结点数: ";cin>>N;d = new int*[N];cout<<"输入边信息矩阵:"<<endl;for(i=0;i<N;i++){d[i] = new int[N];for(j=0;j<N;j++)cin>>d[i][j];}nodetag = new NODEtag[N];for(i=0;i<N;i++)//结点标记初始化{nodetag[i].node = 0;nodetag[i].length = (i==0?0:H);nodetag[i].tag = 0;}shortpath(d,N,nodetag);//计算最短路径cout<<endl<<"结点1到其余结点的最短路径信息:"<<endl;for(i=1;i<N;i++)//输出最短路径长度,及走向{cout<<"结点1到"<<i+1<<"长度为:"<<nodetag[i].length<<",走向为:";getpath(nodetag,i);}for(i=0;i<N;i++)//释放内存delete[] d[i];delete[] d;delete[] nodetag;return 0;}

程序运行截图如下,所得结果与之前分析一直。本程序中对2结点间不可达的情况未做处理,大家可以自己补上!

转帖请注明出处:http://blog.csdn.net/love_feng_forever/article/details/21301875




0 0
原创粉丝点击