1. 程式人生 > >使用鄰接矩陣+Dijkstra演算法求解單源最短路徑問題

使用鄰接矩陣+Dijkstra演算法求解單源最短路徑問題

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;
}