力扣刷题记录&整理——(十一)Graphs


前言

整理力扣刷题思路。

  • 语言:python
  • 题库:来自neetcode: link

一、预备知识


二、解题思路

1.DFS

200.number-of-islands

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。
link

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        #搜索岛屿位置直到超过边界或没有陆地
        def dfs(i,j):
            if 0<=i<len(grid) and 0<=j<len(grid[0]) and grid[i][j]=='1':
                grid[i][j]='0'
            else:
                return
            dfs(i-1,j)
            dfs(i+1,j)
            dfs(i,j-1)
            dfs(i,j+1)

        cnt = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]=='1':
                    cnt += 1
                    dfs(i,j)
        return cnt

695.max-area-of-island

给你一个大小为 m x n 的二进制矩阵 grid 。

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。

link

class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        def dfs(i,j):
            if 0<=i<len(grid) and 0<=j<len(grid[0]) and grid[i][j]==1:
                grid[i][j] = 0
                return 1+dfs(i-1,j)+dfs(i+1,j)+dfs(i,j-1)+dfs(i,j+1)
            else:
                return 0
        
        maxm = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]==1:
                    maxm = max(maxm, dfs(i,j))
        return maxm

这一题跟上一题求岛屿个数思路相似

133.clone-graph

给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。

图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。

class Node {
public int val;
public List neighbors;
}
link

"""
# Definition for a Node.
class Node:
    def __init__(self, val = 0, neighbors = None):
        self.val = val
        self.neighbors = neighbors if neighbors is not None else []
"""

from typing import Optional
class Solution:
    def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
        visited = {}
        def dfs(node):
            if not node:
                return
            if node in visited:
                return visited[node]

            cloned_node = Node(node.val,[])
            visited[node] = cloned_node
            for n in node.neighbors:
                cloned_node.neighbors.append(dfs(n))

            return cloned_node

        #由于是连通图,所以搜索一个节点必然会遍历完整个图
        return dfs(node)

417.pacific-atlantic-water-flow

有一个 m × n 的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。

这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n 的整数矩阵 heights , heights[r][c] 表示坐标 (r, c) 上单元格 高于海平面的高度 。

岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。

返回网格坐标 result 的 2D 列表 ,其中 result[i] = [ri, ci] 表示雨水从单元格 (ri, ci) 流动 既可流向太平洋也可流向大西洋 。

link

class Solution:
    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        m,n = len(heights),len(heights[0])
        ao = set()
        po = set()
        
        #ocean代表大西洋或太平洋,从海洋的边界开始搜索,将能流入当前海洋的位置加入集合
        def dfs(i,j,ocean):
            if (i,j) in ocean:
                return
            ocean.add((i,j))
            
            for i1,j1 in [[i-1,j],[i+1,j],[i,j-1],[i,j+1]]:
                #因为是从边界开始搜索,所以新的位置高度应该不小于原位置
                if 0<=i1<m and 0<=j1<n and heights[i][j]<=heights[i1][j1]:
                    dfs(i1,j1,ocean)
            return
        
        #从太平洋边界搜索,也即上、左
        for j in range(n):
            dfs(0, j, po)
        for i in range(m):
            dfs(i, 0, po)
        
        #从大西洋边界搜索,也即下、右
        for j in range(n):
            dfs(m-1, j, ao)
        for i in range(m):
            dfs(i, n-1, ao)
        
        return [list(item) for item in po&ao]

130.surrounded-regions

给你一个 m x n 的矩阵 board ,由若干字符 ‘X’ 和 ‘O’ ,找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
link

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        m,n = len(board),len(board[0])
        if min(m,n)<3:
            return

        def dfs(i,j):
            board[i][j] = 'B'
            for i1,j1 in [(i-1,j),(i+1,j),(i,j-1),(i,j+1)]:
                if 0<=i1<m and 0<=j1<n and board[i1][j1]=='O':
                    dfs(i1,j1)
         
        for i in (0,m-1):
            for j in range(n):
                if board[i][j]=='O':
                    dfs(i,j)
        for j in (0,n-1):
            for i in range(m):
                if board[i][j]=='O':
                    dfs(i,j)
        
        for i in range(m):
            for j in range(n):
                if board[i][j] == 'O':
                    board[i][j] = 'X'
                if board[i][j] == 'B':
                    board[i][j] = 'O'

分析:link

207.course-schedule

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
link

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        #记录当前位置是否被访问过,初始均为未访问
        flag = [0] * numCourses
        #记录以当前次序课程为先修课程的课程的序号
        adjacent = [[] for _ in range(numCourses)]
        for cur,pre in prerequisites:
            adjacent[pre].append(cur)

        def dfs(i,flag,adjacent):
            #若课程i在本轮搜索中被重复访问,则存在环
            if flag[i] == 1:
                return False
            #课程i在之前轮的搜索中被访问过,无需再访问
            if flag[i] == -1:
                return True
            
            flag[i] = 1 #标记为1,代表在本轮访问过
            for cur in adjacent[i]:
                if not dfs(cur,flag,adjacent):
                    return False
            flag[i] = -1 #遍历结束无环则标记为-1
            return True

        for i in range(numCourses):
            if not dfs(i,flag,adjacent):
                return False
        return True

参考:link

210.course-schedule-ii

现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。

例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。
link

class Solution:
    def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
        flag = [0]*numCourses
        adjacent = [[] for _ in range(numCourses)]
        for cur,pre in prerequisites:
            adjacent[pre].append(cur)
        
        def dfs(i,flag,adjacent):
            if flag[i] == 1:
                return False
            if flag[i] == -1:
                return True
            
            flag[i] = 1
            for cur in adjacent[i]:
                if not dfs(cur,flag,adjacent):
                    return False
            flag[i] = -1
            self.ans = [i] + self.ans
            return True

        self.ans = []
        for i in range(numCourses):
            if not dfs(i,flag,adjacent):
                return []
        return self.ans

684.redundant-connection

树可以看成是一个连通且 无环 的 无向 图。

给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。

请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的那个。
link

class Solution:
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        #这里的dfs返回true代表从节点x有另外的路径连接到节点y
        #再结合x到y的边,就形成了环
        def dfs(x,y):
            visited.add(x)
            for val in mapping[x]:
                if val==y:
                    return True
                if val not in visited:
                    if dfs(val,y):
                        return True
            return False

        #mappping记录每个节点直接连接的节点
        mapping = {}
        for edge in edges:
            if edge[0] not in mapping:
                mapping[edge[0]] = {edge[1]}
            else:
                visited = set()
                if dfs(edge[0],edge[1]):
                    return edge
                else:
                    mapping[edge[0]].add(edge[1])
            if edge[1] not in mapping:
                mapping[edge[1]] = {edge[0]}
            else:
                mapping[edge[1]].add(edge[0])

2.BFS

200.number-of-islands

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        #与DFS的思路一致
        def bfs(i,j):
            queue = [[i,j]]
            while queue:
                i,j = queue.pop(0)
                if 0<=i<len(grid) and 0<=j<len(grid[0]) and grid[i][j]=='1':
                    grid[i][j] = '0'
                    queue += [i-1,j],[i+1,j],[i,j-1],[i,j+1]

        cnt = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]=='1':
                    cnt += 1
                    bfs(i,j)
        return cnt

695.max-area-of-island

class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        def bfs(i,j):
            queue = [[i,j]]
            cnt = 0
            while queue:
                i,j = queue.pop(0)
                if 0<=i<len(grid) and 0<=j<len(grid[0]) and grid[i][j]==1:
                    grid[i][j] = 0
                    cnt += 1
                    queue += [i-1,j],[i+1,j],[i,j-1],[i,j+1]
            return cnt
        
        maxm = 0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]==1:
                    maxm = max(maxm, bfs(i,j))
        return maxm

133.clone-graph

"""
# Definition for a Node.
class Node:
    def __init__(self, val = 0, neighbors = None):
        self.val = val
        self.neighbors = neighbors if neighbors is not None else []
"""

from typing import Optional
class Solution:
    def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
        visited = {}

        def bfs(node):
            if not node:
                return
            
            cloned_node = Node(node.val,[])
            visited[node] = cloned_node
            queue = [node]
            while queue:
                cur = queue.pop(0)
                for n in cur.neighbors:
                    if n not in visited:
                        visited[n] = Node(n.val,[])
                        queue.append(n)
                    visited[cur].neighbors.append(visited[n])
            return cloned_node

        return bfs(node)

417.pacific-atlantic-water-flow

class Solution:
    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        m,n = len(heights),len(heights[0])
        ao = set()
        po = set()
        
        def bfs(x,y,ocean):
            queue = [[x,y]]
            ocean.add((x,y))
            while queue:
                i,j = queue.pop(0)
                for i1,j1 in [[i-1,j],[i+1,j],[i,j-1],[i,j+1]]:
                    if 0<=i1<m and 0<=j1<n and heights[i][j]<=heights[i1][j1] and (i1,j1) not in ocean:
                        queue.append([i1,j1])
                        ocean.add((i1,j1))
            return
        
        for j in range(n):
            bfs(0, j, po)
            bfs(m-1, j, ao)
        for i in range(m):
            bfs(i, 0, po)
            bfs(i, n-1, ao)
        
        return [list(item) for item in po&ao]

130.surrounded-regions

class Solution:
    def solve(self, board: List[List[str]]) -> None:
        """
        Do not return anything, modify board in-place instead.
        """
        m,n = len(board),len(board[0])
        if min(m,n)<3:
            return

        def bfs(i,j):
            board[i][j] = 'B'
            queue = [(i,j)]
            while queue:
                i,j = queue.pop(0)
                for i1,j1 in [(i-1,j),(i+1,j),(i,j-1),(i,j+1)]:
                    if 0<=i1<m and 0<=j1<n and board[i1][j1]=='O':
                        board[i1][j1] = 'B'
                        queue.append((i1,j1))
         
        for i in (0,m-1):
            for j in range(n):
                if board[i][j]=='O':
                    bfs(i,j)
        for j in (0,n-1):
            for i in range(m):
                if board[i][j]=='O':
                    bfs(i,j)
        
        for i in range(m):
            for j in range(n):
                if board[i][j] == 'O':
                    board[i][j] = 'X'
                if board[i][j] == 'B':
                    board[i][j] = 'O'

994.rotting-oranges

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。

返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。
link

class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
        m,n = len(grid),len(grid[0])
        time = 0
        queue = []
        for i in range(m):
            for j in range(n):
                if grid[i][j]==2:
                    queue.append((i,j,time))
        
        while queue:
            i,j,time = queue.pop(0)
            for i1,j1 in [(i-1,j),(i+1,j),(i,j-1),(i,j+1)]:
                if 0<=i1<m and 0<=j1<n and grid[i1][j1]==1:
                    grid[i1][j1]=2
                    queue.append((i1,j1,time+1))
        
        for row in grid:
            if 1 in row:
                return -1
        
        return time

参考:link

207.course-schedule

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        #记录对应次序的课程有几门先修课程
        indegree = [0] * numCourses
        #记录以当前次序课程为先修课程的课程的序号
        adjacent = [[] for _ in range(numCourses)]
        for cur,pre in prerequisites:
            indegree[cur] += 1
            adjacent[pre].append(cur)

        queue = []
        #队列中加入无先修课程的课
        for i in range(numCourses):
            if not indegree[i]:
                queue.append(i)

        while queue:
            pre = queue.pop(0)
            numCourses -= 1
            for cur in adjacent[pre]:
                indegree[cur] -= 1
                if not indegree[cur]:
                    queue.append(cur)
        
        return not numCourses

210.course-schedule-ii

class Solution:
    def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
        indegree = [0]*numCourses
        adjacent = [[] for _ in range(numCourses)]
        for cur,pre in prerequisites:
            indegree[cur] += 1
            adjacent[pre].append(cur)
        
        queue,ans = [],[]
        for i in range(numCourses):
            if not indegree[i]:
                queue.append(i)
                ans.append(i)
        
        while queue:
            pre = queue.pop(0)
            numCourses -= 1
            for cur in adjacent[pre]:
                indegree[cur] -= 1
                if not indegree[cur]:
                    queue.append(cur)
                    ans.append(cur)
        
        return ans if not numCourses else []

684.redundant-connection

class Solution:
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        def bfs(x,y):
            visited.add(x)
            queue = [x]
            while queue:
                cur = queue.pop(0)
                for val in mapping[cur]:
                    if val not in visited:
                        if val==y:
                            return True
                        queue.append(val)
                        visited.add(val)
            return False

        mapping = {}
        for edge in edges:
            if edge[0] not in mapping:
                mapping[edge[0]] = {edge[1]}
            else:
                visited = set()
                if bfs(edge[0],edge[1]):
                    return edge
                else:
                    mapping[edge[0]].add(edge[1])
            if edge[1] not in mapping:
                mapping[edge[1]] = {edge[0]}
            else:
                mapping[edge[1]].add(edge[0])

127.word-ladder

字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> … -> sk:

每一对相邻的单词只差一个字母。
对于 1 <= i <= k 时,每个 si 都在 wordList 中。注意, beginWord 不需要在 wordList 中。
sk == endWord
给你两个单词 beginWord 和 endWord 和一个字典 wordList ,返回 从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0 。
link

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        newlist = set(wordList)
        if endWord not in newlist:
            return 0
        
        visited = {beginWord}
        queue = [(beginWord,1)]
        while queue:
            word,step = queue.pop(0)
            if word == endWord:
                return step
            #每次只改变一个字母,保存在序列中的新单词,并记录转换序列长度
            for i in range(len(beginWord)):
                for j in range(26):
                    tmp = word[:i] + chr(97+j) + word[i+1:]
                    if tmp not in visited and tmp in newlist:
                        queue.append((tmp,step+1))
                        visited.add(tmp)
        return 0

更改为双端BFS

class Solution:
    def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
        newlist = set(wordList)
        if endWord not in newlist:
            return 0
        
        lvisit,rvisit = {beginWord},{endWord}
        lq,rq = [beginWord],[endWord]
        step = 0

        while lq and rq:
            if len(lq)>len(rq):
                lq,rq = rq,lq
                lvisit,rvisit = rvisit,lvisit
            step += 1

            for _ in range(len(lq)):
                word = lq.pop(0)
                if word in rvisit:
                    return step
                    
                for i in range(len(beginWord)):
                    for j in range(26):
                        tmp = word[:i] + chr(97+j) + word[i+1:]
                        if tmp not in lvisit and tmp in newlist:
                            lq.append(tmp)
                            lvisit.add(tmp)
        return 0
  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值