第十一章 圖論 Part1

haohaoscnblogs發表於2024-09-04

任務

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適合的問題:

  1. 路徑查詢:在一些問題中,我們需要找到從起點到終點的一條路徑(或所有路徑)。DFS 很適合這種問題,因為它可以遞迴地探索每一條可能的路徑,直到找到目標為止。
  2. 連通性問題:DFS 可以用於判斷圖是否連通,即圖中是否存在一條從一個節點到另一個節點的路徑。
  3. 拓撲排序:在處理有向無環圖(DAG)時,DFS 可以用於生成拓撲排序
  4. 回溯演算法:之前的回溯演算法章節的問題都可以認為是dfs解決的問題

bfs適合的問題

  1. 最短路徑問題:BFS 是在無權圖中尋找兩點之間最短路徑的最優演算法。因為 BFS 按層次遍歷,所以首次到達目標節點時,一定是透過最短路徑到達的。
  2. 層次遍歷:BFS 按層次逐層遍歷圖或樹結構,非常適合需要按照距離或層次關係處理的場景。
  3. 最小生成樹(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

相關文章