1. 程式人生 > >狄克斯特拉演算法學習小記

狄克斯特拉演算法學習小記

丟擲問題

在廣度優先搜尋演算法裡面我們在從雙子峰到金門大橋的圖裡面尋找的是換乘最短的路徑,但是我們需要找出最快的路徑應該怎麼選擇?
如下圖:
在這裡插入圖片描述

因此我們可以使用另一種演算法——狄克斯特拉演算法(Dijkstra’s algorithm)

演算法簡介

狄克斯特拉演算法包含4個步驟:
1、找出“最便宜”的節點,即可在最短時間內到大的節點。
2、更新該節點的鄰居的開銷,即找出與這個節點有的對映關係的所有節點。
3、重複這個過程,直到對圖中的每個節點都這樣做了。
4、計算最終路徑。

狄克斯特拉演算法用於每條邊都有關聯數字的圖,這些數字稱為權重(weight)

帶權重的圖被稱為加權圖(weighted graph),不帶權重的圖被稱為非加權圖(unweighted graph)。

要計算非加權圖中的最短路徑可以使用廣度優先搜尋,而要計算加權圖中的最短路徑可使用狄克斯特拉演算法。
在有向圖中,每條邊都是單向的;而在無向圖中,每條邊都是一個環。
狄克斯特拉演算法只適用於有向無環圖(directed acyclicgraph, DAG)

當然,如果圖中有負權邊,狄克斯特拉演算法也不適用

程式碼實現

我們以下圖為例使用程式碼來實現一下狄克斯特拉演算法:
在這裡插入圖片描述

首先,我們用三個散列表來分別表示各節點之間的關係和邊的權重:
在這裡插入圖片描述

隨著演算法的進行,我們將不斷的更新散列表costs和parents。

graph = {}
graph['start']['a'] = 6
graph['start']['b'] = 2
graph['a'] = {}
graph['a']['fin'] = 1
graph['b'] = {}
graph['b']['a'] = 3
graph['b']['fin'] = 5
graph['fin'] = {}  # 終點沒有任何鄰居  

# 接下來,用一個散列表來儲存每個節點的開銷  
infinity = float('inf')  # python中無窮大的表示方法  
costs = {}  
costs['a'] = 6
costs['b'] = 2
costs['fin'] = infinity  

# 然後用一個散列表儲存父節點
parents = {}
parents['a'] = "start"
parents['b'] = "start"
parents['fin'] = None  

# 最後,我們需要一個數組用於記錄處理過的節點  
processed = []

# 我們先來實現一下找出開銷最低的節點的方法
def find_lowest_cost_node(costs):  
	lowest_cost = float('inf')  
	lowest_cost_node = None  
	for node in costs:  
		cost = costs[node]  
		if cost < lowest_cost and node not in processed:  
			lowest_cost = cost  
			lowest_cost_node = node  
	return lowest_cost_node

# 接著我們來實現一下演算法  
# 在未處理的節點中找出開銷最小的節點
node = find_lowest_cost_node(costs)
# 處理所有節點  
while node is not None:  
	cost = costs[node]
	neighbors = graph[node]  # 找出這個節點的所有鄰居
	for n in neighbors.keys(): 
		new_cost = cost + neighbors[n]  
		if costs[n] > new_cost:  # 如果經當前節點前往該鄰居更近
			cost[n] = new_cost  # 更新該鄰居的開銷
			parents[n] = node  # 將該鄰居的父節點設定為當前節點  
	processed.append(node)  # 將當前節點標記為已處理過  
	node = find_lowest_cost_node(costs)  # 找出接下來要處理的節點