1. 程式人生 > >最短路徑問題-Dijkstra演算法

最短路徑問題-Dijkstra演算法

前言:

最短路徑演算法用於計算一個節點到其他所有節點的最短路徑。主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。

最短路徑問題是圖論研究中的一個經典演算法問題,是尋找圖(由結點和路徑組成的)中兩結點之間的最短路徑。

文章為了通俗易懂,避免使用一些複雜詞彙,可能會喪失部分表述準確度,但是這樣對我這樣菜的新手更佳友好,文章介紹最為經典的Dijkstra演算法。為了更好的描述演算法增加可讀性,將使用C++作為演算法描述,而且不會考慮演算法優化問題。

// 這一段是廢話

迪傑斯特拉(Dijkstra)演算法是典型的用來解決最短路徑的演算法,也是很多教程中的範例,由荷蘭電腦科學家狄克斯特拉於1959年提出,用來求得從起始點到其他所有點最短路徑。該演算法採用了貪心的思想,每次都查詢與該點距離最近的點,也因為這樣,它不能用來解決存在負權邊的圖。解決的問題大多是這樣的:有一個有向圖G,邊e的長度為l,找出第0點到第N點的最短路徑。

問題描述:

如圖片為有向圖,0點為出發點。

輸入正整數N(0<N<=4)作為終點,請求出從0點到N點的最短路徑長度。

分析:

我們讀入資料就涉及“圖”在計算機中的儲存方式,這裡給出兩個最常用方案:

1.鄰接矩陣

2.鄰接表

為了簡化這篇文章,只介紹方便的鄰接矩陣儲存方式。

鄰接矩陣

鄰接矩陣其實是一個二維陣列。陣列的每一個元素儲存圖中相鄰兩點之間的長度,陣列兩個下標分別表示對應兩點的標號。

就比如題目中給出的圖片,鄰接矩陣是這樣的:

int matrix[5][5] =

{

       { INF, 99, 50, INF, INF },

       { INF, INF, 50, 50, 50 },

       { INF, INF, INF, 99, INF },

       { INF, INF, INF, INF, 75 },

       { INF, INF, INF, INF, INF }

};

圖中共有5個點,所以陣列大小為5x5。

比如0點到1點的距離為99,在鄰接矩陣matrix中,表現為matrix[0][1] == 99;但是因為圖為有向圖,1點不能到0點,matrix[0][1] == INF。

INF表示兩點之間距離無限遠。而且因為一個點到一個點本身沒有任何意義,所以一個點到自己的距離也設定為INF。

鄰接矩陣基本上可以應付大部分的情況,但是你會發現,鄰接矩陣在記憶體空間方面佔用較多,文章給出的例子只有5個定點,需要5x5的int陣列,如果點特別多,而且為有向圖可以考慮使用鄰接表的方式儲存。

下面介紹Dijkstra演算法的大致步驟:

我們首先建立兩個陣列,起名為visited 和distance。visited記錄節點是否被訪問過,distance儲存起點到該點的最短路徑。

(1)   初始化。除了起點,其他點都沒有被訪問過,把每個點到起點從矩陣讀取的路徑存到distance陣列。

(2)   遍歷所有沒有訪問過的點,找到當前distance中儲存最近的路徑的點k,標記該點為已訪問。

(3)   遍歷所有沒有訪問過的點,檢查是起點到每個點近還是k點到每個點近,如果是後者,更新當前點distance,儲存k距起點的距離加上k到每個點的距離。

(4)   重複2,3步驟,直到所有點訪問完成。

#include <cstdio>

const int INF = 0x3f3f3f;

static int matrix[5][5] =
{
	{ INF, 99, 50, INF, INF },
	{ INF, INF, 50, 50, 50 },
	{ INF, INF, INF, 99, INF },
	{ INF, INF, INF, INF, 75 },
	{ INF, INF, INF, INF, INF } 
};

/// dest 是目的地點
///	
int Dijkstra(const int dest) {
	// 頂點的數量
	const int vertex_num = 5;

	// vis陣列是訪問標記陣列,記錄已經訪問的頂點
	// dis陣列儲存起點到當前點的最短路徑
	// 都加1是為了訪問安全,防止編譯器和執行時抽風
	bool vis[vertex_num + 1] = { 0 };
	int  dis[vertex_num + 1] = { 0 };

	// 初始化0點到各點的距離
	for (int i = 1; i < vertex_num; ++i)
		dis[i] = matrix[0][i];

	// 標記0點,表示已經訪問過
	vis[0] = true;

	for (int i = 1; i < vertex_num; ++i) {
		// 查詢最近的點
		int min = INF, k = 0;
		for (int j = 0; j < vertex_num; ++j) {
			if (! vis[j] && dis[j] < min) {
				min = dis[j];
				k = j;
			}
		}
		// 標記查詢到的最近點
		vis[k] = true;

		// 判斷是直接點0到點j短,還是經過點k到點j更短
		for (int j = 1; j < vertex_num; ++j) {
			if (! vis[j] && min + matrix[k][j] < dis[j]) {
				dis[j] = min + matrix[k][j];
			}
		}
	}

	return dis[dest];
}

int main() {
	int n;
	while (~scanf("%d", &n)) {
		printf("%d\n", Dijkstra(n));
	}
	return 0;
}