leetcode刷題記錄:演算法(六)BFS&DFS

子非魚iii發表於2020-11-07

廣度優先搜尋(BFS)和深度優先搜尋(DFS)是兩種常見的圖/樹的搜尋演算法。
廣度優先,即優先搜尋當前根節點的所有子結點;
深度優先,即優先搜尋當前根節點其中的一個子結點,然後再搜尋這個子節點的子節點(套娃),直到當前結點沒有子節點,才返回上一層。

對於兩種搜尋演算法的實現:

廣度優先搜尋使用佇列(queue)來實現,整個過程可以看做一個倒立的樹形:

  1. 把根節點放到佇列的末尾。
  2. 每次從佇列的頭部取出一個元素,檢視這個元素所有的下一級元素,把它們放到佇列的末尾。並把這個元素記為它下一級元素的前驅。
  3. 找到所要找的元素時結束程式。
  4. 如果遍歷整個樹還沒有找到,結束程式。

核心程式碼:

queue=[]
queue.append(root)
while queue:
	root = queue.pop(0)
	for node in root.children:
		queue.append(node)
		print(node)

深度優先搜尋用棧(stack)來實現,整個過程可以想象成一個倒立的樹形:

  1. 把根節點壓入棧中。
  2. 每次從棧中彈出一個元素,搜尋所有在它下一級的元素,把這些元素壓入棧中。並把這個元素記為它下一級元素的前驅。
  3. 找到所要找的元素時結束程式。
  4. 如果遍歷整個樹還沒有找到,結束程式。

以程式碼來實現:

stack=[]
stack.append(root)
while stack:
	root = stack.pop()
	for node in root.children:
		stack.append(node)
		print(node)

另外深度優先搜尋用遞迴來實現更加方便。

接下來看leetcode的練習題。

559.N叉樹的最大深度

這道題用DFS和BFS都可以,編寫要點是在遍歷的過程中記錄深度。
先看BFS

class Solution:
    def maxDepth(self, root: 'Node') -> int:
        if root is None:
            return 0
        queue = []
        # 不為空,當前深度為1
        queue.append((1,root))
        while queue:
            current_depth, root_node = queue.pop(0)
            for a in root_node.children:
                queue.append((current_depth+1, a))
        return current_depth

DFS其實差不多,不過程式碼稍微複雜一點(其實這個也不是真正的DFS)

class Solution:
    def maxDepth(self, root: 'Node') -> int:
        if root is None:
            return 0
        depth = 0
        stack = []
        # 不為空,當前深度為1
        stack.append((1,root))
        while stack:
            current_depth, root_node = stack.pop()
            depth = max(depth, current_depth)
            for a in root_node.children:
                stack.append((current_depth+1, a))
        return depth

用遞迴方式實現DFS:

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: Node
        :rtype: int
        """
        if root is None: 
            return 0 
        elif root.children == []:
            return 1
        else: 
            height = [self.maxDepth(c) for c in root.children]
            return max(height) + 1 

690.員工的重要性
從題目中可以看出,這個資料結構為一棵樹,但是沒有children這樣的屬性,所以為了能夠使用DFS和BFS,需要將資料存到一個字典裡。
之後的問題就變成了,在一個樹形資料結構中,指定一個結點,將這個結點和他的子樹的所有importance全都加起來。

先用BSF:

class Solution:
    def getImportance(self, employees: List['Employee'], id: int) -> int:
        dic = {e.id: e for e in employees}
        queue=[]
        sum_importance = 0
        queue.append(dic[id])
        while queue:
            cur = queue.pop(0)
            sum_importance += cur.importance
            for i in cur.subordinates:
                queue.append(dic[i])
        return sum_importance

然後是遞迴實現的DFS(我自己寫的不夠精簡,copy了一下官方的):

class Solution:
    def getImportance(self, employees: List['Employee'], id: int) -> int:
        dic = {e.id: e for e in employees}
        def dfs(eid):
            employee = dic[eid]
            return (employee.importance +sum(dfs(eid) for eid in employee.subordinates))
        return dfs(id)

200.島嶼數量

給你一個由 ‘1’(陸地)和 ‘0’(水)組成的的二維網格,請你計算網格中島嶼的數量。
島嶼總是被水包圍,並且每座島嶼只能由水平方向和/或豎直方向上相鄰的陸地連線形成。
此外,你可以假設該網格的四條邊均被水包圍。
輸入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
輸出:1

按題目理解,這道題的資料結構可以簡化為一個圖型結構,演算法即為對圖的搜尋。
即對於一個val=1的點(i,j),在圖中,它與(i+1, j),(i-1, j),(i, j+1),(i, j-1)四個點中,val同樣為1的點相連。一個圖即一個島嶼。

那麼統計有多少個島嶼,即統計有多少個完整的圖。

統計思路為:

  1. 遍歷grid
  2. 如果一個點為1,以這個點為起始,執行DFS或者BFS
  3. 在執行的過程中,將1改為0,這樣在遍歷結束後,這個島全為0,不會影響接下來的搜尋
  4. 返回執行DFS/BFS的次數,即為島嶼的個數

按這個思路寫個程式碼,先來一個BFS,有點小長。
時間複雜度O(mn)
空間複雜度也是O(m
n)

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        if grid==[]:
            return 0
        m, n = len(grid), len(grid[0])
        queue = []
        count = 0
        for x in range(0, m):
            for y in range(0, n):
                if grid[x][y] == '1':
                    grid[x][y] = '0'
                    queue.append((x, y))
                    while queue:
                        x_island, y_island = queue.pop(0)
                        for r, c in [(x_island - 1, y_island), (x_island + 1, y_island), (x_island,y_island - 1), (x_island,y_island + 1)]:
                            if 0 <= r < m and 0 <= c < n and grid[r][c] == "1":
                                queue.append((r, c))
                                grid[r][c] = '0'
                    count += 1
        return count

在用深度優先搜尋寫一個:

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        if grid==[]:
            return 0
        m, n = len(grid), len(grid[0])
        count = 0
        for x in range(m):
            for y in range(n):
                if grid[x][y] == '1':
                    count += 1
                    self.dfs(grid, m, n, x, y)
        return count
	# 遞迴的深度優先搜尋
    def dfs(self, grid, m, n, x_island, y_island):
        grid[x_island][y_island] = 0
        for x, y in [(x_island - 1, y_island), (x_island + 1, y_island), (x_island,y_island - 1), (x_island,y_island + 1)]:
            if 0 <= x < m and 0 <= y < n and grid[x][y] == "1":
                self.dfs(grid, m, n, x, y)

相關文章