1.加權圖,非加權圖
說白了,就是在有向圖的邊上加上數字,這個數字可以代表很多東西,如果邊代表路徑,那么數字可以代表這個邊的長度。同時這個數字有專門的術語,叫做權重。要計算非加權圖中的最短路徑,可使用廣度優先搜索。要計算 加權圖中的最短路徑,可使用狄克斯特拉算法。
2.狄克斯特拉算法
狄克斯特拉算法是用來尋找一個加權圖的最短路徑。對於一個加權圖來說,邊最少不代表路程最短。
狄克斯特拉算法包含四個步驟
1 找出“最便宜”的節點,即可在最短時間內到達的節點。
2 更新該節點的鄰居的開銷,其含義將稍后介紹。
3 重復這個過程,直到對圖中的每個節點都這樣做了。
4 計算最終路徑。
3.環
在處理圖的問題時候,可能會遇到環,比如下圖
這意味着你可從一個節點出發,走一圈后又回到這個節點。假 設在下面這個帶環的圖中,你要找出從起點到終點的最短路徑。
當遇到環的時候,從圖中我們可以看到當我們沒繞環一次,都會加8的權重,甚至我們可以繞環多次,所以繞環的路徑不可能是最短路徑,我們應該避開環的路徑。
且我們之前討論到了無向圖,其實無向圖就是環。因為無向圖意味着兩個節點彼此指向對方。
4.負權重
當加權圖中出現了負權重,那么不能使用狄克斯特拉算法,比如下圖,直接兌換海報不需要錢,兌換架子鼓需要35元,而先花5元買唱片,再換海報可以反賺2元,再換架子鼓,總需33元。
如果對於上圖這樣的負加權圖使用狄克斯特拉算法,根據其選擇最小權的方案,他會選擇第一種35元的方案。
在負權圖中,要找出最短路徑,可以使用另一種算法,貝爾曼-福德算法。
5.實現狄克斯特拉算法
利用迪克斯特拉算法實現下面的有向圖
根據該有向圖的箭頭我們可以知道各個節點之間的對應關系應該如下圖
隨着算法的進行,costs和parents這兩個散列表應該是變化的。
# graph表,構建加權圖,對於graph表來說,是散列表嵌套散列表
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2
graph["a"] = {}
graph["a"]["finish"] = 1
graph["b"] = {}
graph["b"]["a"] = 3
graph["b"]["finish"] = 5
# 存儲每個節點的開銷,即消耗的路徑,在python中可以使用infinity = float("inf")來表示無限大。
infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["finish"] = infinity
# 存儲父節點的散列表
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["finish"] = None
# 存儲已經處理過的節點,新建一個數組
processed = []
# 尋找最小開銷節點
def find_min_node(costs): # 尋找當前未被處理過且權重最小的節點
min_weight = infinity
min_node = None
for k, v in costs.items():
if k in processed:
continue
if v < min_weight:
min_weight = v
min_node = k
return min_node
# 狄克斯特拉算法
def Dijkstra_algorithm():
node = find_min_node(costs) # 首先找到當前未被處理且權重最小的節點
while node is not None:
cost = costs.get(node)
neighbors = graph.get(node)
for k in neighbors:
new_cost = cost + neighbors.get(k)
if new_cost < costs.get(k):
costs[k] = new_cost
parents[k] = node
processed.append(node)
node = find_min_node(costs)
Dijkstra_algorithm()
print(costs)
print(parents)