使用廣度優先搜尋 (BFS) 來解無權重圖的最短路徑問題。它的應用場景例如在地圖導航中找到兩地之間的最短路徑。
def shortest_path(graph, start, end):
queue = deque([(start, [start])]) # 使用佇列進行 BFS
visited = set([start])
while queue:
node, path = queue.popleft()
if node == end:
return path # 找到終點,返回路徑
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, path + [neighbor]))
測試
graph_list = {
0: [1, 2],
1: [0, 3],
2: [0, 3],
3: [1, 2]
}
print(“節點 0 到節點 3 的最短路徑:”, shortest_path(graph_list, 0, 3)) # 預期輸出: [0, 1, 3]
程式追踪
初始化:
- 使用
queue佇列來進行 BFS,並將起始節點start和目前路徑[start]作為一個元組放入佇列中。 - 初始化
visited集合來儲存已訪問過的節點,避免重複訪問。 - 起始狀態如下:
queue = deque([(start, [start])]) # 佇列:[(0, [0])] visited = set([start]) # 訪問集合:{0}
- 使用
開始 BFS 搜尋:
- 進入
while迴圈,每次從queue中取出一個節點和該節點的當前路徑(node, path)。 - 首次迭代中,
node為0,path為[0]。
- 進入
檢查終點:
- 如果
node是終點end,則直接返回path,因為 BFS 找到的第一條到達終點的路徑即為最短路徑。 - 在首次迭代中,
node為0,不是終點3,所以繼續進行下一步。
- 如果
遍歷鄰居:
- 取出
node的所有鄰居節點,逐個檢查每個neighbor是否已訪問過:- 若
neighbor未被訪問過,將其加入visited集合,並將(neighbor, path + [neighbor])放入queue中。這樣可以追踪到每個節點的路徑。
- 若
- 在首次迭代中,節點
0的鄰居是1和2,都未被訪問,依序將(1, [0, 1])和(2, [0, 2])加入queue。更新後狀態如下:queue = deque([(1, [0, 1]), (2, [0, 2])]) visited = {0, 1, 2}
- 取出
重複步驟 2-4:
- 再次從
queue中取出下一個節點,直到找到終點或queue為空。 - 第二次迭代時,取出
(1, [0, 1]),檢查node為1的鄰居0和3。0已訪問過,略過。3未被訪問過,加入visited並將(3, [0, 1, 3])放入queue中。
- 當取出
(3, [0, 1, 3])時,發現node已為end,所以返回[0, 1, 3]作為最短路徑。
- 再次從
結果
- 測試輸出:
[0, 1, 3],表示節點0到節點3的最短路徑。
這段程式運用 BFS 的層級遍歷特性,確保找到的第一條符合條件的路徑即為最短路徑。queue 負責儲存當前節點及其累計路徑,visited 避免節點重複訪問。
補充說明
在這段程式中,deque([(start, [start])]) 的寫法是正確的,原因在於:
deque([(start, [start])])使用的是一個列表[(start, [start])],該列表包含一個元組(start, [start])。這樣可以初始化deque時,一次就放入多個元素,並保持每個元素的結構。deque((start, [start]))則會導致錯誤,因為deque()會將整個元組(start, [start])視為一個可迭代對象。這樣會將start和[start]當作分開的兩個元素放入deque中,而不是一個完整的元組。
deque([(start, [start])]) 的用法可以將 deque 初始化為包含單個元組的結構,而 deque((start, [start])) 則會將元組拆開,導致錯誤。
沒有留言:
張貼留言