leetcode刷題記錄:演算法(六)BFS&DFS
廣度優先搜尋(BFS)和深度優先搜尋(DFS)是兩種常見的圖/樹的搜尋演算法。
廣度優先,即優先搜尋當前根節點的所有子結點;
深度優先,即優先搜尋當前根節點其中的一個子結點,然後再搜尋這個子節點的子節點(套娃),直到當前結點沒有子節點,才返回上一層。
對於兩種搜尋演算法的實現:
廣度優先搜尋使用佇列(queue)來實現,整個過程可以看做一個倒立的樹形:
- 把根節點放到佇列的末尾。
- 每次從佇列的頭部取出一個元素,檢視這個元素所有的下一級元素,把它們放到佇列的末尾。並把這個元素記為它下一級元素的前驅。
- 找到所要找的元素時結束程式。
- 如果遍歷整個樹還沒有找到,結束程式。
核心程式碼:
queue=[]
queue.append(root)
while queue:
root = queue.pop(0)
for node in root.children:
queue.append(node)
print(node)
深度優先搜尋用棧(stack)來實現,整個過程可以想象成一個倒立的樹形:
- 把根節點壓入棧中。
- 每次從棧中彈出一個元素,搜尋所有在它下一級的元素,把這些元素壓入棧中。並把這個元素記為它下一級元素的前驅。
- 找到所要找的元素時結束程式。
- 如果遍歷整個樹還沒有找到,結束程式。
以程式碼來實現:
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的點相連。一個圖即一個島嶼。
那麼統計有多少個島嶼,即統計有多少個完整的圖。
統計思路為:
- 遍歷grid
- 如果一個點為1,以這個點為起始,執行DFS或者BFS
- 在執行的過程中,將1改為0,這樣在遍歷結束後,這個島全為0,不會影響接下來的搜尋
- 返回執行DFS/BFS的次數,即為島嶼的個數
按這個思路寫個程式碼,先來一個BFS,有點小長。
時間複雜度O(mn)
空間複雜度也是O(mn)
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)
相關文章
- LeetCode刷題記錄LeetCode
- leetcode刷題記錄 661~LeetCode
- leetcode刷題記錄:演算法(三)滑動視窗演算法LeetCode演算法
- LeetCode刷題記錄——day1LeetCode
- LeetCode刷題記錄——day2LeetCode
- LeetCode刷題記錄——day3LeetCode
- LeetCode刷題記錄——day4LeetCode
- LeetCode刷題記錄——day5LeetCode
- LeetCode-劍指Offer刷題記錄LeetCode
- LeetCode刷題記錄與題解(C++版本)LeetCodeC++
- 用 Rust 刷 leetcode 第六題RustLeetCode
- BUU刷題記錄
- 記錄刷題日常
- LeetCode 刷題記錄(16、17、18)—Java語言LeetCodeJava
- LeetCode 刷題筆記LeetCode筆記
- leetcode刷題筆記LeetCode筆記
- 面試刷題偶有記錄面試
- [雜項] 刷題記錄
- Codeforces 刷題記錄
- leetcode排序專題演算法刷題LeetCode排序演算法
- 刷題記錄:劍指offer+遇到的筆試題+LeetCode筆試LeetCode
- leetcode刷題筆記605LeetCode筆記
- leetcode刷題記錄1041-1050 python版LeetCodePython
- leetcode每日一題刷題記錄(10.26-10.30)LeetCode每日一題
- 軟考刷題記錄3
- 軟考刷題記錄5
- 劍指offer刷題記錄
- 每週刷個 leetcode 演算法題LeetCode演算法
- 刷題記錄(C語言)01C語言
- ctfshow刷題記錄-cry方向-1
- ctfshow刷題記錄-社工篇-1
- LeetCode解題記錄(貪心演算法)(二)LeetCode演算法
- LeetCode解題記錄(貪心演算法)(一)LeetCode演算法
- leetcode刷題筆記(3)(python)LeetCode筆記Python
- LeetCode刷題筆記9.2-9.9LeetCode筆記
- leetcode刷題筆記8.5-8.9LeetCode筆記
- [LeetCode刷題筆記] 關於LeetCode的前言LeetCode筆記
- LeetCode刷題記13-27. 移除元素LeetCode