广度优先搜索算法BFS讲解以及python 实现
一.圖簡介
假設(shè)你居住在舊金山,要從雙子峰前往金門大橋,你想乘公交車前往。
為找出換乘最少的乘車路線,你將使用怎樣的算法?
金門大橋未突出,因此一步無法到達(dá)那里。兩步能嗎?
金門大橋未突出,兩步步無法到達(dá)那里。三步能嗎?
金門大橋突出了!因此從雙子峰出發(fā),可沿下面的路線三步到達(dá)金門大橋。
還有其他前往金門大橋的路線,但他們更遠(yuǎn)(需要四步)。這個(gè)算法發(fā)現(xiàn),前往金門大橋的最短路徑需要三步,這種問題被稱為最短路徑問題。
解決最短路徑的算法被稱為廣度優(yōu)先搜索。
需要兩個(gè)步驟:
1圖
圖模擬一組連接。假設(shè)你和你朋友正在玩牌,模擬下誰欠誰錢。
Alex欠Rama錢,Tom欠Adit錢,等等。圖由節(jié)點(diǎn)(node)和邊(edge)組成。
圖由節(jié)點(diǎn)和邊組成。一個(gè)節(jié)點(diǎn)可以由眾多節(jié)點(diǎn)直接相連組成。這些節(jié)點(diǎn)被稱為鄰居。在前面的欠錢圖中,Rama是Alex的鄰居,Adit不是Alex的鄰居,因?yàn)樗鼈儾恢苯酉噙B,但Adit是Rama的鄰居,又是Tom的鄰居。
二.廣度優(yōu)先搜索
廣度優(yōu)先搜索通常用來解決兩類問題:
算例
本文通過算法來解決問題1。
假設(shè)你經(jīng)營著一個(gè)芒果農(nóng)場,需要尋找芒果銷售商,以便將這些芒果賣給他。為此你可以在你的朋友中查找。
首先創(chuàng)建一個(gè)朋友名單,然后依次檢查名單中的每一個(gè)人,看他是不是芒果銷售商。
假設(shè)你的朋友沒有人是芒果銷售商,你必須在朋友的朋友中尋找。
檢查名單中的每個(gè)人時(shí),你都將其朋友加入名單
這樣一來,你不僅在朋友中查找,還在朋友的朋友中查找。
上文解釋了如何解決問題1,下面講解如何通過算法來解決問題2。
誰是最近的芒果銷售商,例如,朋友是一度關(guān)系,朋友的朋友是二度關(guān)系。
在你看來,一度關(guān)系勝過二度關(guān)系,首先在一度關(guān)系里尋找,如果沒有芒果銷售商,再在二度關(guān)系里尋找,以此類推。
廣度優(yōu)先搜索就是這樣干的,首先檢查一度關(guān)系,再檢查二度關(guān)系。
在本題中,你可以這樣理解,一度關(guān)系先于二度關(guān)系加入名單。你按順序檢查名單中的每一個(gè)人,看他是不是芒果銷售商,然后再在二度關(guān)系里找。
本題需要運(yùn)用到一個(gè)知識(shí)點(diǎn):隊(duì)列
隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu),具有先進(jìn)先出的特點(diǎn)。例如排隊(duì)上車,排在前面的先上車。
三.代碼實(shí)現(xiàn)
1. 使用圖來建立問題模型。
首先,需要用代碼來實(shí)現(xiàn)圖。圖是有節(jié)點(diǎn)和鄰近節(jié)點(diǎn)組成。來表示 [我:余登武] ,[我的鄰居:小明,小白] 這種映射關(guān)系。這需要用到散列表。
散列表可以將鍵映射到值,在這里,將你映射到你的朋友。
python中用字典表示散列表。
graph={} graph['you']=["alice","bob","claire"]更大的圖
2.使用廣度優(yōu)先搜索解決問題
算法流程
流程中用到了隊(duì)列。隊(duì)列是一種數(shù)據(jù)結(jié)構(gòu),具有先進(jìn)先出的特點(diǎn)。例如排隊(duì)上車,排在前面的先上車。python中采用deque來創(chuàng)建一個(gè)隊(duì)列。
代碼
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # @Author: yudengwu(余登武) # @Date : 2021/1/04 #@email:1344732766@qq.com#定義圖 graph={} graph['you']=["alice","bob","claire"] graph['bob']=["anuj","peggy"] graph['alice']=["peggy"] graph['claire']=["thom","jonny"] graph['anuj']=[] graph['peggy']=[] graph['thom']=[] graph['jonny']=[]#檢查是否是芒果銷售商。如果名字最后一個(gè)字母為m就是芒果銷售商 def person_is_seller(name):return name[-1] =='m'#from collections import deque #導(dǎo)入隊(duì)列 search_deque=deque()#創(chuàng)建一個(gè)隊(duì)列 search_deque+=graph['you'] #print(search_deque) #deque(['alice', 'bob', 'claire'])#檢查部分代碼 try:while search_deque: # 只有隊(duì)列不為空find = False # 定義是否找到person = search_deque.popleft() # 取出隊(duì)列中的第一個(gè)人if person_is_seller(person):print(person + "是芒果銷售商")find = Truebreakelse:search_deque += graph[person] # 將朋友的朋友添加進(jìn)隊(duì)列 finally:if find==False:print('沒有找到')運(yùn)行結(jié)果
該段代碼終止條件:
代碼優(yōu)化
代碼優(yōu)化
peggy既是Alice的朋友又是Bob的朋友,因此她將被加入隊(duì)列兩次:一次是在添加Alice的朋友時(shí),一次是在添加Bob的朋友時(shí)。因此搜索隊(duì)列里包含了兩個(gè)peggy。
我們可以只檢查peggy一次,檢查完一個(gè)人后,標(biāo)記這個(gè)人為已檢查。且不再檢查他。如果不這樣做,很快可能陷入死循環(huán),如果你的朋友關(guān)系如圖。
最后代碼
#定義圖,圖中每個(gè)人的朋友關(guān)系都要寫 graph={} graph['you']=["alice","bob","claire"] graph['bob']=["anuj","peggy"] graph['alice']=["peggy"] graph['claire']=["thom","jonny"] graph['anuj']=[] graph['peggy']=[] graph['thom']=[] graph['jonny']=[]#檢查是否是芒果銷售商。如果名字最后一個(gè)字母為m就是芒果銷售商 def person_is_seller(name):return name[-1] =='m'#檢查是否是芒果商代碼from collections import deque #導(dǎo)入隊(duì)列 search_deque=deque()#創(chuàng)建一個(gè)隊(duì)列def search(name):search_deque = deque() # 創(chuàng)建一個(gè)隊(duì)列search_deque += graph[name]#將一級(jí)關(guān)系添加進(jìn)隊(duì)列,即自己的朋友searched=[]#定義是否檢查try:while search_deque: # 只有隊(duì)列不為空find = False # 定義是否找到person = search_deque.popleft() # 取出隊(duì)列中的第一個(gè)人if person not in searched:#如果這個(gè)人沒有被檢查if person_is_seller(person):#如果這個(gè)人是芒果商print(person + "是芒果銷售商")find = Truebreakelse: #如果不是芒果商search_deque += graph[person] # 將朋友的朋友添加進(jìn)隊(duì)列searched.append(person)#將這個(gè)人標(biāo)記為已檢查finally:if find == False:print('沒有找到')search('you')運(yùn)行結(jié)果
打印最短路徑
在上文中,我們找到了芒果供應(yīng)商是thom,我們現(xiàn)在需要把you到thom的關(guān)系打印出來。
我這里采用倒排表的知識(shí)來實(shí)現(xiàn)
假設(shè)
原始字典為
{‘問題IID’:[關(guān)鍵詞1,關(guān)鍵詞2…],‘問題2ID’:[關(guān)鍵詞2,關(guān)鍵詞3…]…}
處理后的倒排表為
{‘關(guān)鍵詞1’:[問題1ID],‘關(guān)鍵詞2’:[問題1ID,問題2ID…}
本文有借鑒書籍《算法圖解》
作者:電氣-余登武
總結(jié)
以上是生活随笔為你收集整理的广度优先搜索算法BFS讲解以及python 实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 结构性存款是理财吗
- 下一篇: 用图讲解狄克斯特拉(DiskStra)算