1. 程式人生 > >演算法基礎【6】單源最短路徑——詳解Bellman-Ford、迪傑斯特拉演算法

演算法基礎【6】單源最短路徑——詳解Bellman-Ford、迪傑斯特拉演算法

首先我們構造研究物件:計算從V0開始到所有節點的最短路徑

1、dijkstra,D演算法

  • 首先我們將需要計算最小路徑的入口點的Cost複製到一個D數組裡。(鄰接矩陣對應的行)
  • 我們知道第一個節點到達的各個頂點所需的花費(路程)(無法到達花費是正無窮)
  • 找到最近的那個點。存下來(如果我要從已更新的點集中邁出第一步,那麼我至少要走多遠。)
  • 我們有了兩個已知的節點作為中介,當更新第三個點的時候,我們就有了一箇中轉節點。
  • 我們比較直接到達和經過中轉節點到達哪一個更近然後不停的邁出最小的步數,我們對每一個節點都計算這樣的路徑,並從這些路徑中選擇一個最小的加入到我們已知的點集之中。用一個數組來記錄所有更新完成的狀態D[i]表示到第i號節點的最短路徑。(用已知的最短路徑去更新子圖
  • 所有節點均被使用後,演算法執行完成。
  • 查詢:輸入一個Aim,直接輸出D[Aim]就可以得到結果。

總結下來就是,不停的更新D陣列直到無法再次更新後,D陣列就是我們想要的答案。如果有點懵,就來看演算法。在大腦裡跑一遍就明白了

經過優化後的演算法(可以直接當做演算法模板來使用。):

#include<iostream>
#include<algorithm>
#define inf 0x3f3f3f3f//表示正無窮
using namespace std;
int cost[6][6] = {//cost[i][j]表示從i到j的花費
	inf, 12, 10,inf, 30,100,
	inf,inf,  5,inf,inf,inf,
	inf,inf,inf, 50,inf,inf,
	inf,inf,inf,inf, 20, 10,
	inf,inf,inf,inf,inf, 60,
	inf,inf,inf,inf,inf,inf,
};
bool used[6];//核心部分開始
int D[6];
int V = 6;//大V表示點數
void Dijkstra(int s)
{
	fill(D, D + V, inf);
	fill(used, used + V, false);
	D[s] = 0;//讓v從s開始
	while (true)
	{
		int v = -1;//小v為當前訪問的點
		for (int u = 0; u < V; u++)
		{
			if (!used[u] && (v == -1 || D[u] < D[v]))v = u;
		}
		if (v == -1)break;
		used[v] = true;
		for (int u = 0; u < V; u++) {
			D[u] = min(D[u], D[v] + cost[v][u]);//中轉到達和直達的花費取最小值,第一遍更新因為D[s]=0,演算法就會將s號節點的Cost陣列複製到D中
		}
	}
}//核心部分結束
int main()
{
	Dijkstra(0);
	for (int i = 0; i < V; i++)
	{
		cout << i << ":" << D[i] << endl;
	}
	int Aim;//查詢
	cin >> Aim;
	cout << D[Aim] << endl;
}

D演算法適合於方便構造Cost矩陣的情況

演算法複雜度O(|V|)

2、Bellma-Ford演算法

這個演算法更加典型,他適合於知道邊集而不方便構造鄰接矩陣的情況。

  • 對邊進行遍歷
  • 比較直達和中轉的路徑花費,取最小值填進去。
void Bellman_ford(int s)
{
	bool upgrade = false;
	fill(D, D + V, inf);
	D[s] = 0;
	while (true)
	{
		for (int i = 0; i < E; i++)
		{
			edge e = es[i];
			if (D[e.from] != inf && D[e.to] > D[e.from] + e.cost)
			{
				D[e.to] = D[e.from] + e.cost;
				upgrade = true;
			}
		}
		if (!upgrade) break;//如果沒有更新就break
	}
}
演算法複雜度 O(|V|*|E|)。