使用鄰接矩陣+Dijkstra演算法求解單源最短路徑問題
阿新 • • 發佈:2018-12-09
Dijkstra演算法是求解有向帶權圖中某一結點到其它結點的最短路徑演算法。這個演算法和Prim演算法求解最小生成樹有點相似,它也是先有一個初始頂點,然後查詢最小帶權路徑。
不同的是,Prim需要更新最小生成樹的結點,不斷將結點更新到VT中,然後更新low_cost[]陣列,是VT中的各結點到V-VT中的各結點的最小路徑。
而Dijkstra演算法更新的dist[]陣列,僅僅是源點到其他點的最短路徑,不需要記錄VT,但可以新增path陣列或容器,記錄其路徑。演算法結束時,dist[]陣列即記錄了源點到其它各點的最短路徑,時間複雜度為O(|V|²)
#include <iostream> #include <string> #include <vector> #include <climits> using namespace std; typedef struct{ int vexnum; //頂點數 int arcnum; //邊數 unsigned **arc; //鄰接矩陣 string *name; }Matrix_Graph; bool newGraph(Matrix_Graph& g) { cout << "-------準備使用鄰接矩陣法建立有向帶權圖-------" << endl; cout << "請輸入頂點數和邊數(空格隔開): "; cin >> g.vexnum >> g.arcnum; // 圖可以沒有邊,但不能沒有頂點 // 若無向圖有n個頂點,則最多有n*(n-1)/2條邊(無向完全圖) if( g.vexnum<0 || g.arcnum<=0 || g.arcnum>(g.vexnum*(g.vexnum-1)/2) ){ cerr << "資料輸入有誤,請檢查資料!" << endl; g.vexnum = g.arcnum = 0; return false; } // 鄰接矩陣初始化 g.arc = new unsigned*[g.vexnum+1]; g.name = new string[g.vexnum+1]; for(int i = 1; i <= g.vexnum; ++i){ g.name[i] = "V" + to_string(i); //從1開始計數,邊為V1, V2, V3... g.arc[i] = new unsigned[g.vexnum+1]; for(int j = 1; j <= g.vexnum; ++j) g.arc[i][j] = INT_MAX; //各邊距離初始化為無窮大 } // 輸入各邊的權值 int vstart, vend, weight; int n = g.arcnum; cout << "請輸入" << n << "條有向邊的起始頂點、終止頂點和權值(空格隔開)" << endl; while( n-- ){ cin >> vstart >> vend >> weight; g.arc[vstart][vend] = weight; //有向圖 } return true; } void printRes(vector<string>& path, int *dist, int cost) { cout << "當前路徑為:"; for(auto s : path) cout << s << ", "; cout << endl; cout << "源點到各點的最短路徑值:" << endl; for(int i = 1; i <= dist[0]; ++i){ if( dist[i] == INT_MAX ) cout << "∞ "; else cout << dist[i] << " "; } cout << endl; cout << "當前路徑權值為:" << cost << endl; cout << endl; } bool Dijkstra(const Matrix_Graph& g, int start = 1) { int i, j, k; // 儲存源點到其它頂點的最短路徑長度 int dist[g.vexnum+1]; for(i = 1; i <= g.vexnum; ++i) dist[i] = g.arc[start][i]; dist[0] = g.vexnum; // 儲存已經訪問過的結點 bool visited[g.vexnum+1] = {false}; visited[start] = true; // 儲存路徑 vector<string> path; //VT path.push_back( g.name[start] ); int n = g.vexnum-1, cost = 0; while( n-- ) { printRes(path, dist, cost); int min = INT_MAX; // 找出dist[]中的最小權值頂點Vj for(i = 1; i <= g.vexnum; ++i){ if( !visited[i] && dist[i]<min ){ min = dist[i]; j = i; } } visited[j] = true; cost += min; path.push_back( g.name[j] ); // 更新dist[] for(k = 1; k <= g.vexnum; ++k){ // 若源點到Vj的距離+Vj到Vk的距離 // 小於源點到Vk的距離,更新 // 至少有一個用unsigned儲存,否則可能會上溢 if( dist[j]+g.arc[j][k] < dist[k] ) dist[k] = dist[j]+g.arc[j][k]; } } cout << "最後的結果是:" << endl; printRes(path, dist, cost); } int main() { Matrix_Graph g; if( newGraph(g) ){ Prim(g); } return 0; }