286. Walls and Gates

286. Walls and Gates

題目給定一個矩陣,矩陣內有標記了牆與門,剩餘的點被標註成一個無限大的數值,意義為可以行走的點,要求把這些矩陣中除了門與牆以外的點,以該點到達附近最近的門的距離為何?

這個題目與 1091. Shortest Path in Binary Matrix 很類似,要矩陣中有牆與路,要找出最短路線,在該題中,題目有設定好起點與終點的位置,所以可以使用廣度優先搜索的方式,逐步找出最短路徑。

雖然與 1091 題類似,但是我們不能使用 1091 的方法來做,因為這樣會使我們要遍歷每一個點後,才能完全證明所有的點都更新成了最短路徑的數值。而這樣的做法其實會有很多不必要的計算,例如從 A 點到 B 點,只有一條路線時,行經中間的點 k 其已經知道其到 B 的最短路徑是 A 到 B 的距離,減去 A 到 k 的距離。

所以這個題目可以反著做,直接從門出發,並且通過 BFS 的方式,逐步更新每個點與自己的距離,比較要注意的點是,當我看到下一個前往的點,該點已經有到附近的門的距離的值時,除非我前往這個點的距離,會比他原始的數字小,我才能更新該點,不然的話該點已經存在著一條更短的路徑了。

我一開始的做法是把 BFS 的邏輯抽出來寫如下,但是這樣做會超時,因為其實這樣做除了 BFS 的時間複雜度以外,多數重複的路徑會一直走,像是在第一個門在探索的時候,探索的過程會到非常遠的地方,並確保所有值都被更新後才會停止,但是因為很多的點已經距離很遠了,其他的門探索的時候很容易又把這些點給更新掉...一直要到後期,才會有機會減少重複探索的次數。

class Solution:
    def wallsAndGates(self, rooms: List[List[int]]) -> None:
        """
        Do not return anything, modify rooms in-place instead.
        """  
        directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
        def bfs(row, col):
            queue = deque()
            queue.append((row, col))
            while queue:
                row, col = queue.popleft()
                for dr, dc in directions:
                    nr = row + dr
                    nc = col + dc
                    if 0 <= nr < rows and 0 <= nc < cols and rooms[nr][nc] > rooms[row][col]:
                        rooms[nr][nc] = rooms[row][col] + 1
                        queue.append((nr, nc))

        rows = len(rooms)
        cols = len(rooms[0])

        for row in range(rows):
            for col in range(cols):
                if rooms[row][col] == 0:
                    bfs(row, col)

觀察題目,其實我們並不侷限一定要一個一個門的去探索,在實作 BFS 時,可以把所有的起點一同放在一起,多個點同時出發,同時一起探索,這樣的話速度會快很多,因為更多的點同時被更新了,會減少很多重複探索的情況。

class Solution:
    def wallsAndGates(self, rooms: List[List[int]]) -> None:
        """
        Do not return anything, modify rooms in-place instead.
        """  

        rows = len(rooms)
        cols = len(rooms[0])
        queue = deque()
        for row in range(rows):
            for col in range(cols):
                if rooms[row][col] == 0:
                    queue.append((row, col))

        directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]

        while queue:
            row, col = queue.popleft()
            for dr, dc in directions:
                nr = row + dr
                nc = col + dc
                if 0 <= nr < rows and 0 <= nc < cols and rooms[nr][nc] > rooms[row][col]:
                    rooms[nr][nc] = rooms[row][col] + 1
                    queue.append((nr, nc))