《算法圖解》學習筆記習題和代碼(第六章 廣度優先搜索)Python3

目錄

6.1 圖簡介 

6.2 圖是什麼 

6.3 廣度優先搜索 

6.3.1 查找最短路徑 

6.3.2 隊列 

 練習1

6.4 實現圖 

 6.5 實現算法 

實現圖和廣度優先搜索代碼

廣度優先搜索的運行時間

練習

6.6 小結 


第六章  廣度優先搜索

用新的數據結構圖來建立網絡模型。

學習廣度優先搜索。(第一種圖算法——廣度優先搜索(breadth-first search,BFS)。 )

學習有向圖和無向圖。

學習拓撲排序,這種排序算法指出了節點之間的依賴關係。

 

廣度優先搜索讓你能夠找出兩樣東西之間的最短距離,不僅僅是狹義上的距離,含義很多:

6.1 圖簡介 

假設你要從雙子峯去金門大橋,爲找出換乘最少的乘車路線,你將使用什麼樣的算法? 

 接下來按照下面這個思路分析:

一步能到達金門大橋嗎?不能。那標出一步能到達的地方。

 兩步能到達金門大橋嗎?標出兩步能到達的地方。

 三步能到達金門大橋嗎?可以。那麼到達金門大橋換乘最少的路線已經找出來,需要三步。其他能到達金門大橋的路線會更遠,需要四步。

這種問題被稱爲最短路徑問題(shorterst-path problem)。解決最短路徑問題的算法被稱爲廣度優先搜索。 

確定如何從雙子峯前往金門大橋,需要兩個步驟。 
(1) 使用圖來建立問題模型。 
(2) 使用廣度優先搜索解決問題。

6.2 圖是什麼 

圖用於模擬不同的東西是如何相連的。

繪製一個欠錢圖。比如下圖表示Alex欠Rama錢。 

完整的欠錢圖可能類似於下面這樣:

 圖由節點(node)和(edge)組成。 一個節點可能與衆多節點直接相連,這些節點被稱爲鄰居。圖用於模擬不同的東西是如何相連的。

6.3 廣度優先搜索 

廣度優先搜索是一種用於圖的查找算法,可幫助回答兩類問題。 

第一類問題:從節點A出發,有前往節點B的路徑嗎
第二類問題:從節點A出發,前往節點B的哪條路徑最短

解決第一類問題,有沒有路徑,例子:

我想看我的朋友有沒有人是芒果經銷商,我就列一個朋友清單,一個一個找。

如果沒有朋友是芒果經銷商,那就在朋友的朋友中找。在檢查每個朋友時,如果他不是,就把他的朋友也加入到查找清單。

不僅在朋友中查找,還在朋友的朋友中查找,使用這種算法將搜遍你的整個人際關係網,直到找到芒果銷售商。這就是廣度優先搜索算法。  

6.3.1 查找最短路徑 

解決第二類問題:最短路徑,例子:

誰是關係最近的芒果售商。例如,朋友是一度關係,朋友的朋友是二度關係。

一度關係勝過二度關係,二度關係勝過三度關係,以此類推。因此,你應先在一度關係中搜索,確定其中沒有芒果銷售商後,纔在二度關係中搜索。廣度優先搜索就是這樣做的。

廣度優先搜索不僅查找從A到B的路徑,而且找到的是最短的路徑。 

只有按添加順序查找時,才能實現這樣的目的。有一個可實現這種目的的數據結構,那就是隊列(queue)。 

6.3.2 隊列 

隊列類似於棧,你不能隨機地訪問隊列中的元素。隊列只支持兩種操作:入隊和出隊。 

先加入的元素將在後加入的元素之前出隊。(先加入的元素先出隊)

隊列是一種先進先出(First In First Out,FIFO)的數據結構,而棧是一種後進先出(Last In First Out,LIFO)的數據結構。  

 練習1

對於下面的每個圖,使用廣度優先搜索算法來找出答案。 
6.1 找出從起點到終點的最短路徑的長度。 

答:最短路徑長度爲2。

6.2 找出從cab到bat的最短路徑的長度。  

答:最短路徑長度爲2。 

6.4 實現圖 

用代碼來實現圖。

每個節點都與鄰近節點相連,如果表示類似於“你→Bob”這樣的關係呢?要將節點映射到其所有鄰居。,如下圖:

表示這種映射關係的Python代碼如下:

graph = {}
graph['you'] = ["alice", "bob", "claire"]

graph["you"]是一個數組,其中包含了“你”的所有鄰居。  

那更大的圖怎麼表示,用代碼表示下面這張圖就是:

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"] = [] 

有向圖和無向圖

有向圖單箭頭,關係是單向的。無向圖沒有箭頭,直接相連的節點互爲鄰居。例如,下面兩個圖是等價的。 

 6.5 實現算法 

這種算法的工作原理。:

 這就是入隊出隊,也叫作壓入彈出。

首先,創建一個隊列。在Python中,可使用函數deque來創建一個雙端隊列。 

我們來編寫查找芒果商的代碼

實現圖和廣度優先搜索代碼

#python 內置函數創建隊列
#在Python中,可使用函數deque來創建一個雙端隊列。 
#創建關係圖
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"] = [] 

from collections import deque

def search(name):
    search_que = deque()
    search_que += graph[name]
    searched = [] #用於記錄檢查過的人
    while search_que:           #只要search_que不爲空
            person = search_que.popleft()    #取出隊列第一個人
            if not person in searched:     
                if person_is_seller(person):      #判斷是不是芒果商的函數
                    print(person+" is a seller")      #是芒果商
                    return True
                    
                else:
                    search_que += graph[person]   #不是芒果商就把他的所有朋友加入搜索隊列
                    searched.append(person)
    return False                         #如果運行到這裏,就說明隊列中沒有芒果商

def person_is_seller(name):
    return name[-1] == 'm'          #我們這裏假設以m結尾的姓名,是芒果商 
search('you')

OUT: 

thom is a seller

True

廣度優先搜索的運行時間

要在人際關係網中沿每條邊搜索芒果商,運行時間至少爲O(邊數)。

當遇到不是芒果商的人,還要把他認識的人再加入到搜索隊列中。將一個人添加到隊列需要的時間是固定的,即爲O(1),因此對每個人都這樣做需要的總時間爲O(人數)。

所以,廣度優先搜索的運行時間爲O(人數 + 邊數),這通常寫作O(V + E),其中V爲頂點(vertice)數,E爲邊數。 

練習

下面的小圖說明了我早晨起牀後要做的事情。 

 該圖指出,我不能沒刷牙就喫早餐,因此“喫早餐”依賴於“刷牙”。 
另一方面,洗澡不依賴於刷牙,因爲我可以先洗澡再刷牙。根據這個圖,可創建一個列表,指出我需要按什麼順序完成早晨起牀後要做的事情: (1) 起牀 (2) 洗澡 (3) 刷牙 (4) 喫早餐 

請注意,“洗澡”可隨便移動,因此下面的列表也可行: 
(1) 起牀 (2) 刷牙 (3) 洗澡 (4) 喫早餐 

6.3 請問下面的三個列表哪些可行、哪些不可行? 

答:A不可行,喫早餐依賴於刷牙。C不可行,洗澡依賴於先起牀。

6.4 下面是一個更大的圖,請根據它創建一個可行的列表。 

答:1.起牀 2.刷牙 3.鍛鍊 4.洗澡5. 穿衣服 6. 喫早餐 7.打包午餐

課後答案: 1——起牀,2——鍛鍊,3——洗澡,4——刷牙,5——穿衣服,6——打包午餐,7——喫早餐。

 

從某種程度上說,這種列表是有序的。如果任務A依賴於任務B,在列表中任務A就必須在任務B後面。這被稱爲拓撲排序,使用它可根據圖創建一個有序列表。假設你正在規劃一場婚禮,並有一個很大的圖,其中充斥着需要做的事情,但卻不知道要從哪裏開始。這時就可使用拓撲排序來創建一個有序的任務列表。  

假設你有一個家譜。 

這是一個圖,因爲它由節點(人)和邊組成。其中的邊從一個節點指向其父母,但所有的邊都往下指。在家譜中,往上指的邊不合情理!(如下圖)因爲你父親不可能是你祖父的父親! 

這種圖被稱爲樹。樹是一種特殊的圖,其中沒有往後指的邊。 

6.5 請問下面哪個圖也是樹? 

 答:A,C是樹,B有往上指的。C是一棵橫着的樹。樹是圖的子集,因此樹都是圖,但圖可能是樹,也可能不是。 

6.6 小結 

 廣度優先搜索指出是否有從A到B的路徑。
如果有,廣度優先搜索將找出最短路徑。
面臨類似於尋找最短路徑的問題時,可嘗試使用來建立模型,再使用廣度優先搜索來解決問題。
有向圖中的邊爲箭頭,箭頭的方向指定了關係的方向,例如,rama→adit表示rama欠adit錢。
無向圖中的邊不帶箭頭,其中的關係是雙向的,例如,ross - rachel表示“ross與rachel約會,而rachel也與ross約會”。
隊列是先進先出(FIFO)的。
棧是後進先出(LIFO)的。
你需要按加入順序檢查搜索列表中的人,否則找到的就不是最短路徑,因此搜索列表必須是隊列
對於檢查過的人,務必不要再去檢查,否則可能導致無限循環。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章