任務
797. 所有可能的路徑
給你一個有 n 個節點的 有向無環圖(DAG),請你找出所有從節點 0 到節點 n-1 的路徑並輸出(不要求按特定順序)
graph[i] 是一個從節點 i 可以訪問的所有節點的列表(即從節點 i 到節點 graph[i][j]存在一條有向邊)。
思路
題目所給的圖的表示是鄰接表,題意就是找到從i到n的所有路徑,這裡使用dfs。參考之前的回溯章節,區別是當時的各種情況的出發根節點可以認為是虛擬節點,在迴圈和遞迴中處理各種情況,而現在的出發節點是實際的圖的點,需要提前加入到path中來保證正確。
class Solution:
def __init__(self):
self.result = []
self.path = []
def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:
self.path.append(0)
self.dfs(graph,0,len(graph)-1)
return self.result
def dfs(self,graph,x,n): # 從x出發到n
if x == n:
self.result.append(self.path[:])
return
for i in graph[x]:
self.path.append(i)
self.dfs(graph,i,n)
self.path.pop()
下面給出輸入為 鄰接矩陣的演算法
class Solution:
def __init__(self):
self.result = []
self.path = []
def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:
self.path.append(0)
self.dfs(graph,0,len(graph)-1)
return self.result
def dfs(self,graph,x,n): # 從x出發到n
if x == n:
self.result.append(self.path[:])
return
for i in graph[x][i]:
if graph[x][i] == 1:
self.path.append(i)
self.dfs(graph,i,n)
self.path.pop()
深度有限搜尋(dfs)和廣度優先搜尋(bfs) 簡析
可以認為樹中的 先中後序遍歷為dfs的特例,層序遍歷為bfs的特例。
dfs適合的問題:
- 路徑查詢:在一些問題中,我們需要找到從起點到終點的一條路徑(或所有路徑)。DFS 很適合這種問題,因為它可以遞迴地探索每一條可能的路徑,直到找到目標為止。
- 連通性問題:DFS 可以用於判斷圖是否連通,即圖中是否存在一條從一個節點到另一個節點的路徑。
- 拓撲排序:在處理有向無環圖(DAG)時,DFS 可以用於生成拓撲排序
- 回溯演算法:之前的回溯演算法章節的問題都可以認為是dfs解決的問題
bfs適合的問題
- 最短路徑問題:BFS 是在無權圖中尋找兩點之間最短路徑的最優演算法。因為 BFS 按層次遍歷,所以首次到達目標節點時,一定是透過最短路徑到達的。
- 層次遍歷:BFS 按層次逐層遍歷圖或樹結構,非常適合需要按照距離或層次關係處理的場景。
- 最小生成樹(MST):在一些圖演算法中,BFS 可以用來生成最小生成樹(雖然更常用的是 Prim 或 Kruskal 演算法)。
bfs的參考程式碼
from collections import deque
# 表示四個方向
dir = [(0, 1), (1, 0), (-1, 0), (0, -1)]
def bfs(grid, visited, x, y):
# 定義佇列
que = deque()
# 起始節點加入佇列
que.append((x, y))
# 只要加入佇列,立刻標記為訪問過的節點
visited[x][y] = True
while que:
# 從佇列取元素
curx, cury = que.popleft() # 當前節點座標
# 開始向當前節點的四個方向左右上下去遍歷
for i in range(4):
nextx = curx + dir[i][0]
nexty = cury + dir[i][1] # 獲取周邊四個方向的座標
# 座標越界了,直接跳過
if nextx < 0 or nextx >= len(grid) or nexty < 0 or nexty >= len(grid[0]):
continue
# 如果節點沒被訪問過
if not visited[nextx][nexty]:
# 佇列新增該節點為下一輪要遍歷的節點
que.append((nextx, nexty))
# 只要加入佇列立刻標記,避免重複訪問
visited[nextx][nexty] = True