資料結構——Floyd演算法
阿新 • • 發佈:2018-12-16
演算法的思想:
遍歷每個結點。然後以這個結點為中間結點來更新所有的結點。
edge(I,j) = min( edge( I , k ) + edge( k , j ) , edge( I , j ) )
edge就是邊的長度
例如:
首先 以 1 為中間結點,更新(1,2),(1,3)(1,4)(1,5)(1,6)(2,3)(2,4)……等所有結點
其次,在以2為中間結點,更新(1,2),(1,3)(1,4)(1,5)(1,6)(2,3)(2,4)……等所有結點
再者,在以3為中間結點,更新(1,2),(1,3)(1,4)(1,5)(1,6)(2,3)(2,4)……等所有結點
以至於後面所有的結點。
在這裡需要用到一個數組來記錄前繼結點。
這個演算法的主要形式就是三層迴圈。從外到內在這裡依次為一,二,三,層迴圈。
第一層迴圈是中間結點。
第二層迴圈0是起始結點
第三層迴圈是結束結點。
需要定義一個path[][]陣列。初始化為每對結點的終止結點,例如(i,j)那就在i,j對應的path陣列中對應的值為j。
初始化path陣列如下:
三層迴圈如下:
程式碼如下;
// // main.cpp // Floyd // // Created by 橘子和香蕉 on 2018/12/15. // Copyright © 2018 橘子和香蕉 All rights reserved. // /* 1:求各個頂點之間的最短路徑 時間複雜度是n*3 2:從圖的鄰接矩陣出發, 還是和之前的演算法一樣,找一箇中間結點來更新所有的結點。假如 v到 u最短距離,還有幾個結點,比如是m,n,b 遍歷這個結點,比如現在用m來更新所有的結點,看距離是不是短的,要是比之前短,就更新,否則,就不要更新, 就是。D(u,v) = min(D(u,v),D(u,m)+D(m,v)) 這個演算法三層迴圈,中間結點在最外面的一層。因為這樣的才可以以他為中心,來遍歷所有的結點 */ #include <iostream> using namespace std; #define VERTEXNUM 100 #define INT_MAX 9999 class Graph{ private: char vertex[VERTEXNUM];//頂點表 int edge[VERTEXNUM][VERTEXNUM];//邊表 int vertexNum;//頂點個數 int edgeNum;//邊的個數 int locate(char data);//在頂點表中找data的位置 void initEdge(); public: Graph(int vertexNum,int edgeNum); void create(); void Floyd(char start ,char end); void printGraph();//輸出 }; void Graph::printGraph(){ cout<<endl; cout<<endl; cout<<"頂點邊:\n"; cout<<"vertexNum:"<<vertexNum<<" edgeNum:"<<edgeNum<<endl; for (int i = 0; i<vertexNum; i++) { cout<<vertex[i]<<"\t"; } cout<<endl; cout<<"邊表如下:\n"; for (int j = 0; j<vertexNum; j++) { for (int k = 0; k<vertexNum ; k++) { cout<<edge[j][k]<<"\t"; } cout<<endl; } } int Graph::locate(char data){ for (int i = 0; i<vertexNum;i++) { if(vertex[i] == data){ return I; } } return -1; } Graph::Graph(int vertexNum,int edgeNum){ this->vertexNum = vertexNum; this->edgeNum = edgeNum; initEdge(); } void Graph::create(){ cout<<"input Graph data\n"; for (int i = 0; i<vertexNum; i++) { cin>>vertex[I]; } char start ,end; int wieght = -1; for (int j = 0; j<edgeNum; j++) { cout<<"input start and end of edge:\n"; cin>>start>>end>>wieght; int startPosition = locate(start); int endPosition = locate(end); edge[startPosition][endPosition] = wieght; edge[endPosition][startPosition] = wieght; } } void Graph:: initEdge(){ for (int i = 0; i<vertexNum; i++) { for (int j =0 ; j<=i; j++) { edge[i][j] = INT_MAX; edge[j][i] = INT_MAX; } } for (int i = 0; i<vertexNum; i++) { for (int j = 0; j<vertexNum; j++) { cout<<edge[i][j]<<"\t"; } cout<<endl; } } void Graph::Floyd(char start,char end){ int path[vertexNum][vertexNum];//定義路徑陣列 for (int i = 0; i<vertexNum; i++) {//初始化,預設i到j的中間結點是j for (int j = 0; j<vertexNum; j++) { path[i][j] = j; } } for (int k = 0; k < vertexNum; k++) { for (int i = 0; i < vertexNum; i++) { for (int j = 0; j < vertexNum; j++) { if( edge[i][k]+edge[k][j] < edge[i][j]){ edge[i][j] = edge[i][k]+edge[k][j]; path[i][j] = path[i][k]; } } } } cout<<"每一對頂點的路徑如下"; int k = -1; for (int i = 0; i < vertexNum; i++) { for (int j = i+1; j < vertexNum; j++) { cout<<"<"<<vertex[i]<<":"<<vertex[j]<<">\t"; k = path[i][j]; cout<<vertex[i]<<"\t"; while (k != j) { cout<<vertex[k]<<"\t"; k = path[k][j]; } cout<<endl; } } cout<<endl; cout<<"path如下\n"; for (int i = 0; i < vertexNum; i++) { for (int j = 0; j < vertexNum; j++) { cout<<path[i][j]<<"\t"; } cout<<endl; } cout<<"要查詢的"<<start<<"到"<<end<<"的路徑如下\n"; int startPosition = locate(start); int endPosition = locate(end); cout<<"<"<<start<<":"<<end<<">\t"<<start<<"\t"; k = path[startPosition][endPosition]; while (k != endPosition) { cout<<vertex[k]<<"\t"; k = path[k][endPosition]; } } int main(){ Graph a(6, 8); a.create(); a.printGraph(); a.Floyd('1', '2'); }
path陣列中儲存的是前一個結點的位置
那怎麼輸出呢?
如下所示:
測試的時候是上面的圖。
例如要查詢 1 到 2 的對應的最短路徑
先去查詢path陣列中,1 ,2 對應的值,結果是4,這就說明4是第一個中間結點,繼續查詢 path 中,4,2對應的項,發現是3,這就說明3是 2 到4 的中間結點。也就是1 到 2 的第二個中間結點,繼續查詢3 到 2 在path對應的項,發現是2,這就說明沒有中間結點了。
輸出程式碼如下:
這裡的圖的無向帶權圖,程式碼執行結果如下;