求全源最短路径可以采用单源最短路径算法实现,如采用贪心算法的Dijkstra,时间开销为|V|*O(E+VlgV),动态规划的Bellman-Ford, |V|*O(V pow 2 *E),还有Bellman的优化算法SPFA。但是呢,这样无疑会在时间开销上花费昂贵。
假设我们采用Bellman-Ford算法,
for (int i = 1; i < numOfVertex; i ++) //这里循环从1开始是因为开始节点已经存放在S中了,还有numOfVertex-1个节点要处理 { for(int start=0;start<numOfVertex;start++) { for (int j =0; j < numOfVertex; j ++) { if (j!= startVertex && map[start][j] < INT_MAX) //在Q中,有距离的为c->d,c->e, c->b { int currentdist = distance[ start] + map[ start ][ j ]; if (currentdist < distance[ j ]) //distance[j = ]为开始到j的距离 { distance[ j ] = currentdist; prevVertex[ j ] = start; } } } } }
看看矩阵的乘法:
Floyd算法可以说是Warshall算法的扩展,三个for循环就可以解决问题,所以它的时间复杂度为O(n^3)。
Floyd算法的基本思想如下:从任意节点A到任意节点B的最短路径不外乎2种可能,1是直接从A到B,2是从A经过若干个节点X到B。所以,我们假设Dis(AB)为节点A到节点B的最短路径的距离,对于每一个节点X,我们检查Dis(AX) + Dis(XB) < Dis(AB)是否成立,如果成立,证明从A到X再到B的路径比A直接到B的路径短,我们便设置Dis(AB) = Dis(AX) + Dis(XB),这样一来,当我们遍历完所有节点X,Dis(AB)中记录的便是A到B的最短路径的距离。
但是这里我们要注意循环的嵌套顺序,如果把检查所有节点X放在最内层,那么结果将是不正确的,为什么呢?因为这样便过早的把i到j的最短路径确定下来了,而当后面存在更短的路径时,已经不再会更新了。
给出如下的代码:
#include <iostream> #include <iostream> #include <vector> #include <stack> using namespace std; int map[][6] = { //定义无向图,或者有向图 {0, 6, 3, INT_MAX, INT_MAX, INT_MAX}, {6, 0, 2, 5,INT_MAX, INT_MAX}, {3, 2, 0, 3,4, INT_MAX}, {INT_MAX,5, 3, 0, 2, 3}, {INT_MAX,INT_MAX, 4, 2, 0, 5}, {INT_MAX,INT_MAX,INT_MAX,3,5,0} }; void Dijkstra( const int numOfVertex, /*节点数目*/ const int startVertex, /*源节点*/ int (map)[][6], /*有向图邻接矩阵*/ int *distance, /*各个节点到达源节点的距离*/ int *prevVertex /*各个节点的前一个节点*/ ) { vector<bool> isInS; //是否已经在S集合中 isInS.reserve(0); isInS.assign(numOfVertex, false); //初始化,所有的节点都不在S集合中 , 分配numOfVertex个字节 //step1 /*初始化distance和prevVertex数组*/ for(int i =0; i < numOfVertex; ++i) { distance[ i ] = map[ startVertex ][ i ]; //源节点到各个节点的距离,其中INT_MAX代表不可达 if(map[ startVertex ][ i ] < INT_MAX) //如果是可达的 prevVertex[ i ] = startVertex; else prevVertex[ i ] = -1; //表示还不知道前一个节点是什么 } prevVertex[ startVertex ] = -1; //源节点无前一个节点 /*开始使用贪心思想循环处理不在S集合中的每一个节点*/ isInS[startVertex] = true; //开始节点放入S集合中 int currentVertex = startVertex; for (int i = 1; i < numOfVertex; i ++) //这里循环从1开始是因为开始节点已经存放在S中了,还有numOfVertex-1个节点要处理 { //step2 /*在Q中选择u到j的distance最小的一个节点, 如第一步A到C,最后目标到D*/ int minDistance = INT_MAX; for(int j = 0; j < numOfVertex; ++j) // { if((isInS[j] == false) && (distance[j] < minDistance))//寻找初始currentVertexA到Q中distance最小的节点 最后为新的currentVertexC { currentVertex = j; minDistance = distance[j]; } } isInS[currentVertex] = true;//将这个节点currentVertex放入S集合中 //step3 /*对这个新的currentVertexC做松弛计算,更新distance*/ for (int j =0; j < numOfVertex; j ++) { if (isInS[j] == false && map[currentVertex][j] < INT_MAX) //在Q中,有距离的为c->d,c->e, c->b { int currentdist = distance[ currentVertex] + map[ currentVertex ][ j ]; if (currentdist < distance[ j ]) //distance[j]为开始到j的距离 { distance[ j ] = currentdist; prevVertex[ j ] = currentVertex; } } } } } void Display(int (arr)[][6], int numOfVertex) { for(int i=0;i<numOfVertex;++i) { for(int j=0;j<numOfVertex;++j) cout<<arr[i][j]<<'\t'; cout<<endl; } } void Floyd( const int numOfVertex, /*节点数目*/ int (map)[][6], /*有向图邻接矩阵,传递二维数组,使用强制类型转换,只需要带入二维数组的指针即可*/ int (distance)[][6], /*各个节点到达源节点的距离*/ int (prevVertex)[][6] /*各个节点的前一个节点*/ ) { for(int i=0;i<numOfVertex;i++) { for(int j=0; j<numOfVertex;j++) { distance[i][j] = map[i][j]; //startVertex = i if(i!=j && map[i][j]<INT_MAX) prevVertex[i][j] = i; else prevVertex[i][j] = -1; } } //add a loop, k //const int a = 2*numOfVertex; int a =0, k=0,c=0; for(; k<numOfVertex && c<1;a++) { k=a%numOfVertex; c=a/numOfVertex; for (int i = 0; i < numOfVertex; i ++) //这里循环从1开始是因为开始节点已经存放在S中了,还有numOfVertex-1个节点要处理 { for (int j =0; j < numOfVertex; j ++) { if (map[k][j] < INT_MAX && distance[i][k]<INT_MAX && i!=j) //start is i, dest is j, k is the road pass through { int newdist = distance[ i][k] + map[k ][ j ]; //add start i if (newdist < distance[i][ j ]) //distance[j]为开始到j的距离 { distance[i][ j ] = newdist; prevVertex[i][ j ] = k; } } } } } } void printPath(int des, int * preVertex) { if(preVertex[des]!=-1) printPath(preVertex[des],preVertex); cout<<des<<' '; } void print(int (pre)[][6], int numOfVertex, int (dist)[][6]) { for(int i=0;i <numOfVertex; i++) { for(int j=0;j<numOfVertex;j++) { printPath(j,pre[i]); cout<<"distance is "<<dist[i][j]<<endl; } } } int main (int argc, const char * argv[]) { int distance[6]; int preVertex[6]; int distanceFloyd[6][6]; int preVertexFloyd[6][6]; for (int i =0 ; i < 6; ++i ) //源目标为i的 { Dijkstra(6, i, map, distance, preVertex); for(int j=0;j<6;++j) { cout<<distance[j]<<' '; } cout<<endl; for(int j =0; j < 6; ++j) { int index = j; //加上for目标为j的 stack<int > trace; while (preVertex[index] != -1) { trace.push(preVertex[index]); index = preVertex[index]; } cout << "路径:"; while (!trace.empty()) { cout<<trace.top()<<" -- "; trace.pop(); } cout <<j; //j cout <<" 距离是:"<<distance[j]<<endl; //j } } cout<<"floyd............."<<endl; Floyd(6, map, distanceFloyd,preVertexFloyd); Display(distanceFloyd,6); cout<<"path"<<endl; print(preVertexFloyd,6,distanceFloyd); system("pause"); return 0; }
即5到节点0之间的距离为无穷。很明显的知道0到5的距离是9,由于这是无向图,5到0的距离也是9,所以将K循环两圈,及c<1改为c<2让他可以来的及更新值,得到如下的结果: