dijkstra算法
dijkstra算法比较容易理解,但是代码有点复杂。
求从源点到其余各点的最短路径的算法的基本思想:
其中,从源点到顶点v的最短路径是所有最短短路径中长度最短者。
路径长度最短的最短路径的特点:
在这条路径上,必定只含一条弧,并且这条弧的权值最小。
下一条路径长度次短的最短路径的特点:
它只可能有两种情况:或者是直接从源点到该点(只含一条弧); 或者是,从源点经过顶点v1,再到达该顶点(由两条弧组成)。
再下一条路径长度次短的最短路径的特点:
它可能有三种情况:或者是,直接从源点到该点(只含一条弧); 或者是,从源点经过顶点v1,再到达该顶点(由两条弧组成);或者是,从源点经过顶点v2,再到达该顶点
其余最短路径的特点:
它或者是直接从源点到该点(只含一条弧); 或者是,从源点经过已求得最短路径的顶点,再到达该顶点
试用范围:
1.dijkstra算法实现单源最短路径查找,路径查找
2.有向图和无向图都可以使用本算法,无向图中的每条边可以看成相反的两条边。
3.用来求最短路的图中不能存在负权边。
具体实现请看算法,下图为简单测试图例
1 #include<iostream> 2 using namespace std; 3 4 const int MAXNUM=101; 5 6 int MAP[MAXNUM][MAXNUM]={ 7 {INT_MAX, 2,INT_MAX,INT_MAX,INT_MAX, 9, 15,INT_MAX,INT_MAX}, 8 {INT_MAX,INT_MAX, 4,INT_MAX,INT_MAX,INT_MAX, 6,INT_MAX,INT_MAX}, 9 {INT_MAX,INT_MAX,INT_MAX, 2,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 15}, 10 {INT_MAX,INT_MAX,INT_MAX,INT_MAX, 1,INT_MAX,INT_MAX,INT_MAX, 1}, 11 {INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 6,INT_MAX, 3,INT_MAX}, 12 {INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 11,INT_MAX}, 13 {INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 15, 2}, 14 {INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 4}, 15 {INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX} 16 }; 17 18 int dist[MAXNUM]; //存储s到各点的最短路径 19 int path[MAXNUM]; //存储s到各点的最短路径中的倒数第二个结点 20 21 #define MAXSUM(a,b) ( ((a)!=INT_MAX&&(b)!=INT_MAX) ? ((a)+(b)) : INT_MAX ) 22 23 void dijkstra(int s,int n) 24 { 25 bool mark[MAXNUM]={false}; //标记s到i是否属于最短路径范畴 26 mark[s]=true; //标记源点s不属于搜索范围 27 int i; 28 for(i=0;i<n;++i) 29 { 30 dist[i]=MAP[s][i]; //初始化s到各点的长度 31 path[i]=s; //path初始化为s,既s为到各结点的路径的倒数第二个结点 32 } 33 for(i=2;i<n;++i) //循环n-2次就可以找出n-1条最短路径 34 { 35 int index=s,lowcost=INT_MAX; 36 int j; 37 38 //在不是最短路径的路径中(mark[j]==false),找出一条最小的路径, 39 //并标记为最新的最短路径(mark[index]=true) 40 for(j=0;j<n;++j) 41 if(mark[j]==false&&dist[j]<lowcost) 42 { 43 index=j; 44 lowcost=dist[j]; 45 } 46 mark[index]=true; 47 //用最新的最短路径更新剩下的非最短路径 48 //既mark[j]==false的路径 49 for(j=0;j<n;++j) 50 if(mark[j]==false&&MAXSUM(dist[index],MAP[index][j])<dist[j]) 51 { 52 //dist[j]=min{dist[j],dist[index]+MAP[index][j]} 53 dist[j]=MAXSUM(dist[index],MAP[index][j]); 54 //确定要更新时,同时更新新路径的倒数第二个结点 55 path[j]=index; 56 } 57 } 58 } 59 60 void reverse(int a[],int n) 61 { 62 for(int i=0,j=n-1;i<j;++i,--j) 63 swap(a[i],a[j]); 64 } 65 66 int getpath(int a[],int s,int i) 67 { 68 int count=1; 69 a[0]=i; 70 while(i!=s) 71 { 72 //path[i]存储s到i路径中i的前驱结点 73 i=a[count++]=path[i]; 74 } 75 reverse(a,count); 76 return count; 77 } 78 79 int main() 80 { 81 int a[MAXNUM]; 82 for(int k=0;k<9;++k) 83 { 84 dijkstra(k,9); 85 for(int i=0;i<9;++i) 86 if(dist[i]!=INT_MAX) 87 { 88 cout<<char(k+'a')<<"到"<<char(i+'a')<<"最短路径长度为:"; 89 cout<<dist[i]<<endl; 90 int length=getpath(a,k,i); 91 for(int j=0;j<length;++j) 92 cout<<char(a[j]+'a')<<' '; 93 cout<<endl<<endl; 94 } 95 } 96 return 0; 97 }
dijkstra算法还有另一种实现,利用队列进行优先级管理,时间复杂度为O((n-2)(n-1)/2),上面dijkstra算法实现的时间复杂度为O((n-2)(n-1)),很明显下面的实现效率是上面的两倍
1 void dijkstra(int s,int n) 2 { 3 int q[MAXNUM],qf=0,ql=0; //建立一个队列 4 int i; 5 for(i=0;i<n;++i) //初始化dis,path,q 6 { 7 dist[i]=MAP[s][i]; 8 path[i]=s; //path初始化为s,既s为到各结点的路径的倒数第二个结点 9 if(i!=s) //将非源点的结点的下标均放进队列 10 q[ql++]=i; 11 } 12 while(qf<ql-1) //循环n-2次就可以完成 13 { 14 //找出dist[q[qf~ql-1]]最小者,并记录队列q的下标 15 int index_min=qf; 16 for(i=qf+1;i<ql;++i) 17 if(dist[q[i]]<dist[q[index_min]]) 18 index_min=i; 19 swap(q[index_min],q[qf]);//保持q[qf]指向最短的,既确定最新的最短路径 20 //如果找出的最短路径是无路径的,那后面的循环毫无意义,可直接跳出 21 if(dist[q[qf]]==INT_MAX) 22 break; 23 //用最新的最短路径dist[q[qf]]来更新dist[q[qf+1~ql-1]] 24 //主意q[qf+1~ql-1]均指向非最短路径的路径 25 for(i=qf+1;i<ql;++i) 26 if(MAXSUM(dist[q[qf]],MAP[q[qf]][q[i]])<dist[q[i]]) 27 { 28 dist[q[i]]=MAXSUM(dist[q[qf]],MAP[q[qf]][q[i]]); 29 path[q[i]]=q[qf]; 30 } 31 ++qf; 32 } 33 }