【日常系列】LeetCode《19·BFS 和 DFS》

数据规模->时间复杂度

<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)

内容

在这里插入图片描述

lc 589 :N 叉树的前序遍历
https://leetcode.cn/problems/n-ary-tree-preorder-traversal/
提示:
节点总数在范围 [0, 104]内
0 <= Node.val <= 104
n 叉树的高度小于或等于 1000
进阶:
递归法很简单,你可以使用迭代法完成此题吗?

#方案一:迭代
"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        if not root:return []
        #
        res=[]
        stack=list()
        stack.append(root)
        while stack:
            curr=stack.pop()
            res.append(curr.val)
            #key
            for i in range(len(curr.children)-1,-1,-1):
                stack.append(curr.children[i])
        #
        return res
#方案二:递归
"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        if not root:return []
        #
        res=[]
        self.dfs(root,res)
        return res
    
    def dfs(self,node,res):
        if not node:return
        res.append(node.val)
        #key
        for children in node.children:
            self.dfs(children,res)
        # for i in range(len(node.children)):
        #     self.dfs(node.children[i],res)

lc 590 :N 叉树的后序遍历
https://leetcode.cn/problems/n-ary-tree-postorder-traversal/
提示:
节点总数在范围 [0, 104] 内
0 <= Node.val <= 104
n 叉树的高度小于或等于 1000
进阶:
递归法很简单,你可以使用迭代法完成此题吗?

#方案一:迭代
"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:
    def postorder(self, root: 'Node') -> List[int]:
        if not root:return []
        res=list()
        stack=[] #list()
        stack.append(root)
        
        while stack:
            curr=stack.pop()
            res.append(curr.val)
            #
            for childnode in curr.children:
                stack.append(childnode) #key
        res.reverse()
        return res
        
#方案二:递归
"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:
    def postorder(self, root: 'Node') -> List[int]:
        res=[]
        self.dfs(root,res)
        return res

    def dfs(self,node,res):
        if not node:return
        #
        for child in node.children:
            self.dfs(child,res)
        res.append(node.val)

lc 429 :N 叉树的层序遍历
https://leetcode.cn/problems/n-ary-tree-level-order-traversal/
提示:
树的高度不会超过 1000
树的节点总数在 [0, 10^4] 之间

#方案一:BFS
"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if not root:return []
        res=[]
        queue=deque()
        queue.append(root)
        while queue:
            level_nodes_val=[]
            for i in range(len(queue)):
                curr=queue.popleft()
                level_nodes_val.append(curr.val) #key
                for child in curr.children:
                    queue.append(child)
            res.append(level_nodes_val)
        return res

#方案二:DFS
"""
# Definition for a Node.
class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
"""

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        res=[]
        self.dfs(root,0,res)
        return res
    
    def dfs(self,node,currlevel,res):
        if not node:return []
        #key
        if len(res)-1 <currlevel:
            res.append([node.val])
        else:
            res[currlevel].append(node.val)
        #
        for child in node.children:
            self.dfs(child,currlevel+1,res)

lc 690 :员工的重要性
https://leetcode.cn/problems/employee-importance/
提示:
一个员工最多有一个 直系 领导,但是可以有多个 直系 下属
员工数量不超过 2000 。
在这里插入图片描述

#方案一:DFS-前序
"""
# Definition for Employee.
class Employee:
    def __init__(self, id: int, importance: int, subordinates: List[int]):
        self.id = id
        self.importance = importance
        self.subordinates = subordinates
"""

class Solution:
    def getImportance(self, employees: List['Employee'], id: int) -> int:
        self.id_map={emp.id:emp for emp in employees} #key:id-emp
        self.total=0 #key
        self.dfs(id)
        return self.total
    
    def dfs(self,id):
        emp=self.id_map[id]
        if not emp:return
        #
        self.total+=emp.importance
        #
        for sub_emp_id in emp.subordinates:
            self.dfs(sub_emp_id)

#方案二:DFS-后序
"""
# Definition for Employee.
class Employee:
    def __init__(self, id: int, importance: int, subordinates: List[int]):
        self.id = id
        self.importance = importance
        self.subordinates = subordinates
"""

class Solution:
    def getImportance(self, employees: List['Employee'], id: int) -> int:
        self.id_map={emp.id:emp for emp in employees} #key:id-emp
        return self.dfs(id)
    
    def dfs(self,id):
        emp=self.id_map[id]
        if not emp:return 0
        #dfs的时候(self.total=0)先累加子节点值,最后在加根节点(root.importance)
        self.total=0  #key:注意位置,
        for sub_emp_id in emp.subordinates:
            self.total+=self.dfs(sub_emp_id) 
            
        return emp.importance+self.total


#方案三:BFS
"""
# Definition for Employee.
class Employee:
    def __init__(self, id: int, importance: int, subordinates: List[int]):
        self.id = id
        self.importance = importance
        self.subordinates = subordinates
"""

class Solution:
    def getImportance(self, employees: List['Employee'], id: int) -> int:
        self.id_map={emp.id:emp for emp in employees} #key:id-emp
        return self.bfs(id)
    
    def bfs(self,id):
        emp=self.id_map[id]
        if not emp:return 0
        #
        res=0
        queue=deque()
        queue.append(emp)
        while queue:
            levelres=0
            for i in range(len(queue)):
                curr=queue.popleft()
                levelres+=curr.importance
                for sub_id in curr.subordinates:
                    queue.append(self.id_map[sub_id]) #key
            res+=levelres
        return res

图的 DFS 和 BFS:key-遍历过程中,设置节点访问

在这里插入图片描述在这里插入图片描述在这里插入图片描述

floodfill 算法基础

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

#数组二维转一维
#数组一维转二维度
#二维数组的四联通
#二维数组的八联通
#保证索引访问合法

lc 733 :图像渲染
https://leetcode.cn/problems/flood-fill/
提示:
m == image.length
n == image[i].length
1 <= m, n <= 50
0 <= image[i][j], newColor < 216
0 <= sr < m
0 <= sc < n
在这里插入图片描述

#方案一:DFS
class Solution:
    def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]:
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1]]
        self.image=image
        self.visited=[[False]*len(self.image[0]) for _ in range(len(self.image))]
        self.oldcolor=self.image[sr][sc]
        
        self.dfs(sr,sc,color)
        return image
    
    def inarea(self,row,col):
        return row>=0 and row<len(self.image) and col>=0 and col<len(self.image[0])
    
    def dfs(self,row,col,newcolor):
        #终止条件
        if not self.inarea(row,col) or self.image[row][col]!=self.oldcolor:
            return
        #
        self.image[row][col]=newcolor
        self.visited[row][col]=True
        for dir in self.dirs: 
            next_row=row+dir[0]
            next_col=col+dir[1]
            if self.inarea(next_row,next_col):
                if not self.visited[next_row][next_col]: 
                    self.dfs(next_row,next_col,newcolor)
#方案二:BFS
class Solution:
    def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]:
        self.image=image
        self.old_color=image[sr][sc]
        if self.old_color == color:  #key
            return image

        self.dirs=[[0,1],[0,-1],[1,0],[-1,0]]
        self.bfs(sr,sc,color)
        return self.image
    
    def inarea(self,row,col):
        return row>=0 and row<len(self.image) and col>=0 and col<len(self.image[0])

    def bfs(self,row,col,newcolor): 
        queue=deque()
        queue.append([row,col])
        self.image[row][col]=newcolor
        while queue:
            curr=queue.popleft()
            #
            for dir in self.dirs: #key1
                nextrow=dir[0]+curr[0]
                nextcol=dir[1]+curr[1]
                if self.inarea(nextrow,nextcol) and self.image[nextrow][nextcol]==self.old_color:
                    queue.append([nextrow,nextcol]) #key1
                    self.image[nextrow][nextcol]=newcolor

lc 463 :岛屿的周长
https://leetcode.cn/problems/island-perimeter/
提示:
row == grid.length
col == grid[i].length
1 <= row, col <= 100
grid[i][j] 为 0 或 1
岛屿中没有“湖”(“湖” 指水域在岛屿内部且不和岛屿周围的水相连(只有一个岛
在这里插入图片描述在这里插入图片描述

#方案一:DFS-前序
class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        self.grid=grid
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1]]
        self.visited=[[False]*len(grid[0]) for _ in range(len(grid))]
        #
        self.ans=0
        for i in range(len(self.grid)):
            for j in range(len(self.grid[0])):
                if self.grid[i][j]==1:
                    self.dfs(i,j)
                    return self.ans
    
    def inarea(self,row,col):
        return row>=0 and row<len(self.grid) and col>=0 and col<len(self.grid[0])

    def dfs(self,row,col):
        if not self.inarea(row,col) or self.grid[row][col]==0 or self.visited[row][col]:
            return
        #
        self.visited[row][col]=True
        for dir in self.dirs:
            nextrow=dir[0]+row
            nextcol=dir[1]+col
            #key:计边长
            if not self.inarea(nextrow,nextcol) or self.grid[nextrow][nextcol]==0:
                self.ans+=1
            elif not self.visited[nextrow][nextcol]:
                self.dfs(nextrow,nextcol)
            
#方案二:DFS-后序
class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        self.grid=grid
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1]]
        self.visited=[[False]*len(grid[0]) for _ in range(len(grid))]
        #
        for i in range(len(self.grid)):
            for j in range(len(self.grid[0])):
                if self.grid[i][j]==1:
                    return self.dfs(i,j)
    
    def inarea(self,row,col):
        return row>=0 and row<len(self.grid) and col>=0 and col<len(self.grid[0])

    def dfs(self,row,col):
        if not self.inarea(row,col) or self.grid[row][col]==0:
            return 1
        if self.visited[row][col]:
            return 0  #说明接壤
        #
        ans=0
        self.visited[row][col]=True
        for dir in self.dirs:
            nextrow=dir[0]+row
            nextcol=dir[1]+col
            ans+=self.dfs(nextrow,nextcol) #key:基于'四边'情况统计
        return ans  #对比lc690

#方案三:BFS
class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        self.grid=grid
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1]]
        self.visited=[[False]*len(grid[0]) for _ in range(len(grid))]
        #
        for i in range(len(self.grid)):
            for j in range(len(self.grid[0])):
                if self.grid[i][j]==1:
                    return self.bfs(i,j)
    
    def inarea(self,row,col):
        return row>=0 and row<len(self.grid) and col>=0 and col<len(self.grid[0])

    def bfs(self,row,col):
        queue=deque()
        queue.append([row,col])
        self.visited[row][col]=True
        #
        ans=0
        while queue:
            curr=queue.popleft()
            for dir in self.dirs:
                nextrow=curr[0]+dir[0]
                nextcol=curr[1]+dir[1]
                if not self.inarea(nextrow,nextcol) or self.grid[nextrow][nextcol]==0:
                    ans+=1
                elif not self.visited[nextrow][nextcol]: #key
                    queue.append([nextrow,nextcol])
                    self.visited[nextrow][nextcol]=True
        return ans     

lc 200 :岛屿数量【top100】
https://leetcode.cn/problems/number-of-islands/
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 ‘0’ 或 ‘1’

#DFS:
class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        self.grid=grid
        self.rows=len(grid)
        self.cols=len(grid[0])
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1]]
        self.visited=[[False]*self.cols for _ in range(self.rows)]
        #
        ans=0
        for i in range(self.rows):
            for j in range(self.cols):
                if self.grid[i][j]=="1" and not self.visited[i][j]:
                    self.dfs(i,j)
                    ans+=1
        #
        return ans

    def inarea(self,row,col):
        return self.rows>row>=0 and self.cols>col>=0

    def dfs(self,row,col):
        if not self.inarea(row,col) or self.grid[row][col]=='0' or self.visited[row][col]:
            return
        self.visited[row][col]=True
        for dir in self.dirs:
            nextrow=row+dir[0]
            nextcol=col+dir[1]
            self.dfs(row,col)

lc 695 :岛屿的最大面积
https://leetcode.cn/problems/max-area-of-island/
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j] 为 0 或 1
在这里插入图片描述

#DFS-后序:
class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        self.grid=grid
        self.rows=len(grid)
        self.cols=len(grid[0])
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1]]
        self.visited=[[False]*self.cols for _ in range(self.rows)]
        #
        maxans=0
        for i in range(self.rows):
            for j in range(self.cols):
                if self.grid[i][j]==1 and not self.visited[i][j]:
                    maxans=max(self.dfs(i,j),maxans)
        #
        return maxans

    def inarea(self,row,col):
        return self.rows>row>=0 and self.cols>col>=0

    def dfs(self,row,col):
        if not self.inarea(row,col) or self.grid[row][col]==0 or self.visited[row][col]:#key:递归中接壤也是0
            return 0
        #
        self.visited[row][col]=True
        ans=0
        for dir in self.dirs:
            nextrow=row+dir[0]
            nextcol=col+dir[1]
            ans+=self.dfs(nextrow,nextcol)
        return 1+ans

lc 130 :被围绕的区域
https://leetcode.cn/problems/surrounded-regions/
提示:
m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j] 为 ‘X’ 或 ‘O’
在这里插入图片描述

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        self.rows=len(board)
        self.cols=len(board[0])
        self.board=board
        self.visited=[[False]*self.cols for _ in range(self.rows)]
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1]]
        #边界
        for j in range(self.cols):
            if self.board[0][j]=='O' and not self.visited[0][j]:self.dfs(0,j)
            if self.board[self.rows-1][j]=='O' and not self.visited[self.rows-1][j]:self.dfs(self.rows-1,j)
        for i in range(1,self.rows-1):
            if self.board[i][0]=='O' and not self.visited[i][0]:self.dfs(i,0)
            if self.board[i][self.cols-1]=='O' and not self.visited[i][self.cols-1]:self.dfs(i,self.cols-1)
        #替换
        for i in range(self.rows):
            for j in range(self.cols):
                if self.board[i][j]=='O' and not self.visited[i][j]:
                    self.board[i][j]='X'
        
    def area(self,row,col):
        return 0<=row<self.rows and 0<=col<self.cols
    def dfs(self,row,col):
        if not self.area(row,col) or self.board[row][col]=='X' or self.visited[row][col]:
            return 
        #
        self.visited[row][col]=True
        for dir in self.dirs:
            nextrow=row+dir[0]
            nextcol=col+dir[1]
            self.dfs(nextrow,nextcol)  

lc 1034 :边框着色
https://leetcode.cn/problems/coloring-a-border/
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 50
1 <= grid[i][j], color <= 1000
0 <= row < m
0 <= col < n
在这里插入图片描述
在这里插入图片描述

class Solution:
    def colorBorder(self, grid: List[List[int]], row: int, col: int, color: int) -> List[List[int]]:
        #
        self.color=color
        self.currcolor=grid[row][col]
        if grid[row][col]==color:return grid
        #
        self.rows=len(grid)
        self.cols=len(grid[0])
        self.grid=grid
        self.visited=[[False]*self.cols for _ in range(self.rows)]
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1]]
        self.dfs(row,col)
        return self.grid
    
    
    def area(self,row,col):
        return 0<=row<self.rows and 0<=col<self.cols
    def dfs(self,row,col):
        if not self.area(row,col) or self.grid[row][col]!=self.currcolor or self.visited[row][col]:
            return 
        #通过(nextrow,nextcol)对当前顶点判定及递归
        self.visited[row][col]=True
        for dir in self.dirs:
            nextrow=row+dir[0]
            nextcol=col+dir[1]
            #key条件
            if not self.area(nextrow,nextcol) or (self.grid[nextrow][nextcol]!=self.currcolor and not self.visited[nextrow][nextcol]):
                self.grid[row][col]=self.color #key-not [nextrow][nextcol]
            #
            self.dfs(nextrow,nextcol)

lc 529 :扫雷游戏
https://leetcode.cn/problems/minesweeper/
提示:
m == board.length
n == board[i].length
1 <= m, n <= 50
board[i][j] 为 ‘M’、‘E’、‘B’ 或数字 ‘1’ 到 ‘8’ 中的一个
click.length == 2
0 <= clickr < m
0 <= clickc < n
board[clickr][clickc] 为 ‘M’ 或 ‘E’
注:E:未挖空 B:已挖空 M:未挖雷 X:已挖雷
在这里插入图片描述

class Solution:
    def updateBoard(self, board: List[List[str]], click: List[int]) -> List[List[str]]:
        self.rows=len(board)
        self.cols=len(board[0])
        self.board=board
        self.visited=[[False]*self.cols for _ in range(self.rows)]
        self.dirs=[[-1,0],[1,0],[0,-1],[0,1],[-1,-1],[-1,1],[1,-1],[1,1]]
        #
        if board[click[0]][click[1]]=='M':#如果一个地雷('M')被挖出,游戏就结束了- 把它改为 'X'
            board[click[0]][click[1]]='X'
        else:
            self.dfs(click[0],click[1])
        return board
        
    def area(self,row,col):
        return 0<=row<self.rows and 0<=col<self.cols
    
    def dfs(self,row,col):
        if not self.area(row,col) or self.board[row][col]!='E' or self.visited[row][col]:
            return 
        #统计雷数
        m_s=0
        for dir in self.dirs:
            nextrow=row+dir[0]
            nextcol=col+dir[1]
            if self.area(nextrow,nextcol) and self.board[nextrow][nextcol]=='M':
                m_s+=1
        #更新board
        #key:雷区是没有必要再递归的
        if m_s>0:
            self.board[row][col]=str(m_s)#如果一个 至少与一个地雷相邻 的空方块('E')被挖出,修改它为数字('1' 到 '8' ),表示相邻地雷的数量。
        else:
            self.board[row][col]='B'
            #key位置:如果一个 没有相邻地雷 的空方块('E')被挖出,修改它为('B'),并且所有和其相邻的 未挖出 方块都应该被递归地揭露。
            for dir in self.dirs:
                nextrow=row+dir[0]
                nextcol=col+dir[1]
                self.dfs(nextrow,nextcol)

lc 994 :腐烂的橘子
https://leetcode.cn/problems/rotting-oranges/
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 10
grid[i][j] 仅为 0、1 或 2
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。
在这里插入图片描述

#多源BFS
class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
        self.dirs = [[-1, 0], [1, 0], [0, -1], [0, 1]]
        self.rows, self.cols = len(grid), len(grid[0])
        self.grid=grid
        #多源
        queue=deque()
        for i in range(self.rows):
            for j in range(self.cols):
                if grid[i][j]==2:
                    queue.append([i,j])
        #
        ans=-1
        while queue:
            for i in range(len(queue)):
                curr=queue.popleft()
                #
                for dir in self.dirs:
                    nextrow,nextcol=curr[0]+dir[0],curr[1]+dir[1]
                    #key
                    if self.area(nextrow,nextcol) and self.grid[nextrow][nextcol]==1:
                        self.grid[nextrow][nextcol]=2
                        queue.append([nextrow,nextcol])
            ans+=1
        #
        for i in range(self.rows):
            for j in range(self.cols):
                if self.grid[i][j]==1:
                    return -1
        return max(ans,0) #[[0]]
    
    
    def area(self,row,col):
        return 0<=row<self.rows and 0<=col<self.cols 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值