1. 程式人生 > >求圖中最短路徑演算法之Dijkstra演算法——C++實現並優化

求圖中最短路徑演算法之Dijkstra演算法——C++實現並優化

Dijkstra演算法是一種比較經典的求圖中最短路徑演算法,它是一種貪心演算法,可以求出從源節點到圖中其他所有節點的最短路徑。適用範圍:用於求有向或無向加權圖中兩點間的最短路徑,其中邊的權值不能為負。

最近重新學習了該演算法,並用C++將其實現,同時對程式碼進行了優化,優化思路如下:

一般的Dijkstra演算法實現程式碼,在每次將節點新增到S集合之前,都要在所有節點中進行搜尋,找出代價最小的節點進行新增。這種方法效率低下,我們可以發現,有兩類節點不是候選節點:1.已經新增到S集合中的節點;2.到源節點代價為無窮的節點。基於這個原因,我將新增到S集合的候選節點儲存在一個連結串列中,並動態的更新,以達到提升程式效率的目的。

由於Dijkstra演算法比較經典,在此不再贅述。下面給出原始碼。

輸入

第一行輸入三個整數n,m,s,分別表示節點個數,邊條數,源節點;接下來輸入m行,每一行輸入三個整數u,v,w,分別表示一條邊的起點,終點,權值;為了便於程式的除錯,可以從檔案中讀取資料;

輸出

輸出從源節點到其他節點的最短路徑以及最小代價,如果不存在則輸出:No path;

原始碼:

#include <iostream>
#include <fstream>
#include <vector>
#include <list>
using namespace std;
//鄰接表中節點,每個節點與該節點對應的索引號指定一條邊
struct Node
{
	int u; //邊終點節點號
	int w; //邊權值
	Node(int a, int b) :u(a), w(b){}
};
struct Record
{
	int pre; //路徑中當前節點的前一節點
	int cost; //當前節點到源節點的最短路徑代價
};
int n, m, s; //n表示圖中節點個數,m表示圖中邊數,s表示源節點
vector<list<Node>> Adj; //圖的鄰接表
vector<Record> Path; //採用雙親表示法儲存源節點到其他所有節點的最短路徑資訊
void Dijkstra()
{	
	vector<bool> isUsed(n, false); //向量某索引號對應的值為true,表示該索引號對應的節點
	//在S集合中
	list<int> Assi; //Assi中儲存著當前的候選節點
	Path.assign(n, Record());
	//路徑資訊初始化
	for (int i = 0; i < n; i++)
	{
		Path[i].pre = i;
		Path[i].cost = INT_MAX;
	}
	isUsed[s] = true;
	for (auto it = Adj[s].begin(); it != Adj[s].end(); it++)
	{
		Path[it->u].pre = s;
		Path[it->u].cost = it->w;
		Assi.push_back(it->u);
	}
	while (!Assi.empty())
	{
		list<int>::iterator It;
		int minCost = INT_MAX;
		//從Assi中選擇代價最小的節點加入到S集合中
		for (auto it = Assi.begin(); it != Assi.end(); it++)
		{
			if (minCost > Path[*it].cost)
			{
				minCost = Path[*it].cost;
				It = it;
			}
		}
		int u = *It;
		Assi.erase(It);
		isUsed[u] = true;
		//對與選中節點直接相連,並且不在S集合中的節點進行鬆弛操作
		//同時更新Assi的內容
		for (auto it = Adj[u].begin(); it != Adj[u].end(); it++)
		{
			if (isUsed[it->u]) continue;
			if (Path[it->u].cost == INT_MAX) Assi.push_back(it->u);
			if (Path[it->u].cost > minCost + it->w)
			{
				Path[it->u].cost = minCost + it->w;
				Path[it->u].pre = u;
			}
		}
	}
}
void Traverse(int k)
{
	if (Path[k].pre == k) { cout << k; return; }
	Traverse(Path[k].pre);
	cout << " " << k;
}
void Print()
{
	cout << "Result:\n";
	for (int i = 0; i < n; i++)
	{
		if (i == s) continue;
		cout << "From " << s << " to " << i << ": ";
		if (Path[i].cost == INT_MAX){ cout << "No path\n\n"; continue; }
		Traverse(i);
		cout << endl;
		cout << "Minimal Cost: " << Path[i].cost << endl << endl;
	}
}
int main()
{
	ifstream in("data.txt"); //從檔案中讀取圖的資訊
	in >> n >> m >> s;
	int u, v, w;
	Adj.assign(n, list<Node>());
	while (m--)
	{
		in >> u >> v >> w;
		Adj[u].push_back(Node(v, w));
	}
	in.close();

	Dijkstra();
	Print();

	system("pause");
	return 0;
}
當用Dijkstra演算法求兩點間的最短路徑時,可以在S集合中添加了終節點後就終止程式,從而提高程式效率。 演算法測試圖: 程式執行結果: