1. 狄克斯特拉算法介紹
適用條件:
1.只適用於有向無環圖(無向圖每條邊都是環)
2.繞環的路徑不可能是最短路徑
3.計算加權圖時用此算法
4. 不支持負權重
計算步驟:
1.找出最短時間內到達的節點
2.若找到更短路徑,則更新到達該節點的鄰居時間
3.重複此過程,直到對每個節點都做過
4.計算最終路徑
如上圖,起點記作start,終點記作 fin,中間節點記作a,b,c,d。權重如邊上數字所示。
目標:找出start到fin的最短路徑(包括權重),並計算最短距離是多少。
#創建散列表來儲存鄰居及其權重
graph={}
graph['start']={}
graph['start']['a']=5
graph['start']['b']=2
graph['a']={}
graph['a']['c']=4
graph['a']['d']=2
graph['b']={}
graph['b']['a']=8
graph['b']['d']=7
graph['c']={}
graph['c']['fin']=3
graph['c']['d']=6
graph['d']={}
graph['d']['fin']=1
graph['fin']={}
##創建開銷表,只創建從start到鄰近節點的costs,其餘節點設置爲inf,並通過後續的迭代進行更新
infinity=float('inf')
costs={}
costs['a']=5
costs['b']=2
costs['c']=infinity
costs['d']=infinity
costs['fin']=infinity
##創建一個存儲父節點的散列表,只存儲start臨近點的父節點記錄,其他的節點父節點暫且
##設置爲None,通過後續的迭代進行更新
parents={}
parents['a']='start'
parents['b']='start'
parents['c']=None
parents['d']=None
parents['fin']=None
記錄結果如下,接下來就是要不斷的更新相應節點的costs和parents
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
processed=[] #記錄已經處理過的節點
node=find_lowest_cost_node(costs) #在未處理的節點中找出開銷最小的節點
while node is not None: #這個wile循環在所有節點都被處理過後結束
cost=costs[node]
neighbors=graph[node]
for n in neighbors.keys(): #遍歷當前節點的所有鄰居
new_cost=cost+neighbors[n]
if costs[n]> new_cost: #如果經當前節點前往該鄰居更近
costs[n]=new_cost #就更新該鄰居的開銷
parents[n]=node #同時將該鄰居的父節點設置爲當前節點
processed.append(node) #將當前節點標記爲處理過
node=find_lowest_cost_node(costs) #找出接下來要處理的節點,並循環
全部跑完之後的結果如下:
可以到看,從起點start到終點fin的最短距離爲8,路徑爲:fin-d-a-start.(由父親節點指向可知)
總結:
- 廣度優先搜索用於在非加權圖中查找最短路徑;
- 狄克斯特拉算法算法用於在加權圖中查找最短路徑;
- 僅當權重爲正時狄克斯特拉算法才管用;
- 如果圖中包含負權邊,請使用貝爾曼-福德算法;
2. 廣度優先搜索介紹
使用隊列(先進先出):
如圖,找出距離中間you(你)最近的符合某個特徵的人,比如,名字長度爲4(爲芒果銷售商)
##廣度優先搜素
graph={}
graph['you']=['alice','bob','claire']
graph['alice']=['peggy']
graph['bob']=['anuj','peggy']
graph['claire']=['thom','jonny']
graph['anuj']=[]
graph['peggy']=[]
graph['thom']=[]
graph['jonny']=[]
from collections import deque
#search_queue = deque() #創建一個隊列
#search_queue += graph['you'] #將你的鄰居都加入到這個搜索隊列中
def person_is_seller(name):
return len(name)==4
def search(name):
search_queue=deque() #創建一個隊列
search_queue += graph[name] #將name的鄰居都加入到這個搜索隊列中
searched=[]
while search_queue: #只要隊列不空
person=search_queue.popleft() #就取出其中的第一個人
if person not in searched: #僅當這個人沒有檢查過時才檢查
if person_is_seller(person):
print(person+" is a mango seller")
return True
else:
search_queue += graph[person] #不符合特徵,將這個人的朋友都加入搜索隊列
searched.append(person) #將這個人標記爲檢查過
return False #如果到了這裏,就說明隊列中沒人是符合特徵
總結:
- 廣度優先搜索指出是否有從A到B的路徑
- 如果有,廣度優先搜索將找出最短路徑
- 面臨類似於尋找最短路徑的問題時,可嘗試使用圖來建立模型,再使用廣度優先搜索來解決問題。
- 隊列是先進先出的(FIFO),可以以queue排隊來記憶,排隊講究先來後到
- 棧是後進先出的(LIFO)的。
- 你需要按加入順序檢查搜索列表中的人,否則找到的就不是最短路徑,因此搜索列表必須是隊列;
- 對於檢查過的人,務必不要再去檢查,否則可能導致無限循環。