前言
整理力扣刷题思路。
- 语言: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 。
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) 流动 既可流向太平洋也可流向大西洋 。
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