-
勇往直前的深度优先遍历:
-
104. 二叉树的最大深度
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def maxDepth(self, root: Optional[TreeNode]) -> int: # 预处理 if not root: return 0 if not root.left and not root.right: return 1 # 正体递归 leftRet = self.maxDepth(root.left) rightRet = self.maxDepth(root.right) # 后处理 return 1 + max(leftRet, rightRet) -
111. 二叉树的最小深度
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def minDepth(self, root: Optional[TreeNode]) -> int: # 预处理 if not root: return 0 if not root.left and not root.right: return 1 # 正体递归 leftRet = self.minDepth(root.left) rightRet = self.minDepth(root.right) # 后处理 if root.left and root.right: return 1 + min(leftRet, rightRet) elif root.left: return 1 + leftRet else: return 1 + rightRet -
112. 路径总和
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: # 预处理 if not root: return False if not root.left and not root.right: return root.val == targetSum # 正体递归 leftRet = self.hasPathSum(root.left, targetSum-root.val) rightRet = self.hasPathSum(root.right, targetSum-root.val) # 后处理 return leftRet or rightRet -
226. 翻转二叉树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: # 预处理 if not root: return root if not root.left and not root.right: return root # 正体递归 leftRet = self.invertTree(root.left) rightRet = self.invertTree(root.right) # 后处理 root.left, root.right = rightRet, leftRet return root -
100. 相同的树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: # 预处理 if not p and not q: return True if p and not q: return False if not p and q: return False if p.val != q.val: return False # 正体递归 leftRet = self.isSameTree(p.left, q.left) rightRet = self.isSameTree(p.right, q.right) # 后处理 return leftRet and rightRet -
101. 对称二叉树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def isSymmetric(self, root: Optional[TreeNode]) -> bool: # 因为该问题没有重复的子问题,所以无法在本身递归,比如你知道左子树和右子树分别是对称的 # 但不能结合起来结合父树的对称问题,所以需要一个辅助dfs def dfs(p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: # 预处理 if not p and not q: return True if not p and q: return False if p and not q: return False if p.val != q.val: return False # 正体递归 leftRet = dfs(p.left, q.right) rightRet = dfs(p.right, q.left) # 后处理 return leftRet and rightRet return dfs(root, root) -
129. 求根节点到叶节点数字之和
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def sumNumbers(self, root: Optional[TreeNode]) -> int: def dfs(node: Optional[TreeNode], path: List[str]) -> int: # 预处理 if not node: return 0 if not node.left and not node.right: path.append(str(node.val)) ans = int("".join(path)) path.pop() return ans # 正体递归 path.append(str(node.val)) leftRet = dfs(node.left, path) path.pop() path.append(str(node.val)) rightRet = dfs(node.right, path) path.pop() # 后处理 return leftRet + rightRet return dfs(root, []) -
236. 二叉树的最近公共祖先
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': # 预处理 if not root: return root if root.val in [p.val, q.val]: return root if not root.left and not root.right: return None # 正体递归 leftRet = self.lowestCommonAncestor(root.left, p, q) rightRet = self.lowestCommonAncestor(root.right, p, q) # 后处理 if leftRet and rightRet: return root elif leftRet: return leftRet else: return rightRet -
105. 从前序与中序遍历序列构造二叉树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: # 预处理 if not preorder or not inorder: return None # 正体递归 rootVal = preorder[0] index = inorder.index(rootVal) leftRet = self.buildTree(preorder[1:index+1], inorder[:index]) rightRet = self.buildTree(preorder[index+1:], inorder[index+1:]) # 后处理 return TreeNode(rootVal, leftRet, rightRet) -
106. 从中序与后序遍历序列构造二叉树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: # 预处理 if not inorder or not postorder: return None # 正体递归 rootVal = postorder[-1] index = inorder.index(rootVal) leftRet = self.buildTree(inorder[:index], postorder[:index]) rightRet = self.buildTree(inorder[index+1:], postorder[index:-1]) # 后处理 return TreeNode(rootVal, leftRet, rightRet) -
1008. 前序遍历构造二叉搜索树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]: # 预处理 if not preorder: return None # 正体递归 rootVal = preorder[0] idx = 0 for i in range(1, len(preorder)): if preorder[i] < rootVal: idx = i leftRet = self.bstFromPreorder(preorder[1:idx+1]) rightRet = self.bstFromPreorder(preorder[idx+1:]) # 后处理 return TreeNode(rootVal, leftRet, rightRet) -
1028. 从先序遍历还原二叉树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def recoverFromPreorder(self, traversal: str) -> Optional[TreeNode]: idx = 0 def dfs(depth: int) -> Optional[TreeNode]: # 预处理 nonlocal idx hyphenCnt = 0 while idx+hyphenCnt < len(traversal) and traversal[idx+hyphenCnt] == "-": hyphenCnt += 1 if depth != hyphenCnt: return None # 正体递归 intEnd = idx + hyphenCnt while intEnd < len(traversal) and traversal[intEnd].isdigit(): intEnd += 1 rootVal = int(traversal[idx+hyphenCnt:intEnd]) idx = intEnd leftRet = dfs(depth + 1) rightRet = dfs(depth + 1) # 后处理 return TreeNode(rootVal, leftRet, rightRet) return dfs(0)
-
数据结构 - 栈
-
144. 二叉树的前序遍历
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: # 预处理 if not root: return [] if not root.left and not root.right: return [root.val] # 正体递归 leftRet = self.preorderTraversal(root.left) rightRet = self.preorderTraversal(root.right) # 后处理 return [root.val] + leftRet + rightRet -
94. 二叉树的中序遍历
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: # 预处理 if not root: return [] if not root.left and not root.right: return [root.val] # 正体递归 leftRet = self.inorderTraversal(root.left) rightRet = self.inorderTraversal(root.right) # 后处理 return leftRet + [root.val] + rightRet -
145. 二叉树的后序遍历
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: # 预处理 if not root: return [] if not root.left and not root.right: return [root.val] # 正体递归 leftRet = self.postorderTraversal(root.left) rightRet = self.postorderTraversal(root.right) # 后处理 return leftRet + rightRet + [root.val] -
589. N 叉树的前序遍历
""" # 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 [] if not root.children: return [root.val] # 正体递归 ret = [] for child in root.children: ret += self.preorder(child) # 后处理 return [root.val] + ret -
590. N 叉树的后序遍历
""" # 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 [] if not root.children: return [root.val] # 正体递归 ret = [] for child in root.children: ret += self.postorder(child) # 后处理 return ret + [root.val]
-
深度优先遍历的应用
-
129. 求根节点到叶节点数字之和
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def sumNumbers(self, root: Optional[TreeNode]) -> int: def dfs(node: Optional[TreeNode], path: List[int]) -> int: # 预处理 if not node: return 0 path.append(node.val) if not node.left and not node.right: ret = int("".join(map(str, path))) path.pop() return ret # 正体递归 leftRet = dfs(node.left, path) rightRet = dfs(node.right, path) path.pop() # 后处理 return leftRet + rightRet return dfs(root, []) -
323. 无向图中连通分量的数目
class Solution: def countComponents(self, n: int, edges: List[List[int]]) -> int: graph = defaultdict(list) for u, v in edges: graph[u].append(v) graph[v].append(u) vis = set() # 这里的辅助dfs只是为了在图中遍历标记路经用,所以无需返回什么 def dfs(node: int): vis.add(node) for nei in graph[node]: if nei not in vis: dfs(nei) ans = 0 for i in range(n): if i not in vis: dfs(i) ans += 1 return ans -
684. 冗余连接
class Solution: def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: def dfs(node: int): vis.add(node) for nei in graph[node]: if nei not in vis: dfs(nei) graph = defaultdict(list) for u, v in edges: if u not in graph or v not in graph: graph[u].append(v) graph[v].append(u) else: vis = set() dfs(u) if v in vis: return [u, v] graph[u].append(v) graph[v].append(u) -
802. 找到最终的安全状态
class Solution: def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]: # 三色问题,再次遇到GRAY时即说明有环,color数组也充当vis角色 # WHITE:未遍历过;GRAY:进入时染色;BLACK:退出时染色 WHITE, GRAY, BLACK = 0, 1, 2 n = len(graph) color = [WHITE] * n # dfs返回该节点是否安全 def dfs(node: int) -> bool: if color[node] != WHITE: return color[node] == BLACK color[node] = GRAY for nei in graph[node]: if not dfs(nei): return False color[node] = BLACK return True return [i for i in range(n) if dfs(i)] -
785. 判断二分图
class Solution: def isBipartite(self, graph: List[List[int]]) -> bool: # 三色问题,如果两相邻节点染的颜色不同,即无法二分,color数组也充当vis角色 # WHITE:未遍历过;GRAY:颜色一;BLACK:颜色二 WHITE, GRAY, BLACK = 0, 1, 2 n = len(graph) color = [WHITE] * n # dfs返回该节点在二分图条件下是否可以被顺利染色 def dfs(node: int, c: int) -> bool: if color[node] != WHITE: return color[node] == c color[node] = c cNei = (BLACK if c == GRAY else GRAY) for nei in graph[node]: if not dfs(nei, cNei): return False return True for i in range(n): if color[i] == WHITE: if not dfs(i, GRAY): return False return True -
210. 课程表 II
class Solution: def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: graph = defaultdict(list) for u, v in prerequisites: graph[v].append(u) # 三色问题,再次遇到GRAY时即说明有环,color数组也充当vis角色 # WHITE:未遍历过;GRAY:进入时染色;BLACK:退出时染色 WHITE, GRAY, BLACK = 0, 1, 2 color = [WHITE] * numCourses ans = [] # dfs返回该节点进入后是否无环,即无法完成全部课程的情况 # ans中依次记录dfs退出时的节点顺序,如果无环,那么退出时的顺序即学习课程的逆序 def dfs(node: int) -> bool: if color[node] != WHITE: return color[node] == BLACK color[node] = GRAY for nei in graph[node]: if not dfs(nei): return False color[node] = BLACK ans.append(node) return True for i in range(numCourses): if color[i] == WHITE: if not dfs(i): return [] return ans[::-1] -
207. 课程表
class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: graph = defaultdict(list) for u, v in prerequisites: graph[v].append(u) # 三色问题,再次遇到GRAY时即说明有环,color数组也充当vis角色 # WHITE:未遍历过;GRAY:进入时染色;BLACK:退出时染色 WHITE, GRAY, BLACK = 0, 1, 2 color = [WHITE] * numCourses # dfs返回该节点进入后是否无环,即无法完成全部课程的情况 def dfs(node: int) -> bool: if color[node] != WHITE: return color[node] == BLACK color[node] = GRAY for nei in graph[node]: if not dfs(nei): return False color[node] = BLACK return True for i in range(numCourses): if color[i] == WHITE: if not dfs(i): return False return True -
1136. 并行课程
class Solution: def minimumSemesters(self, n: int, relations: List[List[int]]) -> int: graph = defaultdict(list) for u, v in relations: graph[u].append(v) # 三色问题,再次遇到GRAY时即说明有环,color数组也充当vis角色 # WHITE:未遍历过;GRAY:进入时染色;BLACK:退出时染色 WHITE, GRAY, BLACK = 0, 1, 2 color = [WHITE] * (n+1) # dfs返回该节点进入后是否无环,即无法完成全部课程的情况 def dfs(node: int) -> bool: if color[node] != WHITE: return color[node] == BLACK color[node] = GRAY for nei in graph[node]: if not dfs(nei): return False color[node] = BLACK return True # 如果无法完成全部课程,提前退出 for i in range(1, n+1): if color[i] == WHITE: if not dfs(i): return -1 node2depth = {} # 求出node节点出发的众多路径的最大深度,即修完该节点出发所需的最大学期数 def dfs1(node: int) -> int: if node in node2depth: return node2depth[node] ret = 0 for nei in graph[node]: depth = dfs1(nei) ret = max(ret, depth) node2depth[node] = ret + 1 return node2depth[node] # 完全部课程所需的最少学期数即为;最长的一条dfs路径的长度 ans = 0 for i in range(1, n+1): if i not in node2depth: depth = dfs1(i) ans = max(ans, depth) return ans -
886. 可能的二分法
class Solution: def possibleBipartition(self, n: int, dislikes: List[List[int]]) -> bool: graph = defaultdict(list) for u, v in dislikes: graph[u].append(v) graph[v].append(u) # 三色问题,再次遇到GRAY时即说明有环,color数组也充当vis角色 # WHITE:未遍历过;GRAY:进入时染色;BLACK:退出时染色 WHITE, GRAY, BLACK = 0, 1, 2 color = [WHITE] * (n+1) # dfs返回该节点在二分图条件下是否可以被顺利染色 def dfs(node: int, c: int) -> bool: if color[node] != WHITE: return color[node] == c color[node] = c cNei = (BLACK if c == GRAY else GRAY) for nei in graph[node]: if not dfs(nei, cNei): return False return True for i in range(1, n+1): if color[i] == WHITE: if not dfs(i, GRAY): return False return True
-
回溯算法
-
51. N 皇后
class Solution: def solveNQueens(self, n: int) -> List[List[str]]: ans = [] cols, slash, backslash = set(), set(), set() def dfs(row: int, path: List[int]): if row >= n: ans.append(path[:]) return for col in range(n): if col in cols or row+col in slash or row-col in backslash: continue cols.add(col), slash.add(row+col), backslash.add(row-col) path.append(col) dfs(row + 1, path) path.pop() cols.remove(col), slash.remove(row+col), backslash.remove(row-col) dfs(0, []) def paint(): board = [] for pattern in ans: for col in pattern: board.append('.'*col + 'Q' + '.'*(n-col-1)) return [board[i:i+n] for i in range(0, len(board), n)] return paint() -
46. 全排列
class Solution: def permute(self, nums: List[int]) -> List[List[int]]: n = len(nums) vis = [False] * n ans = [] def dfs(idx: int, path: List[int]): if idx >= n: ans.append(path[:]) return for i in range(n): if vis[i]: continue vis[i] = True path.append(nums[i]) dfs(idx + 1, path) path.pop() vis[i] = False dfs(0, []) return ans -
37. 解数独
class Solution: def solveSudoku(self, board: List[List[str]]) -> None: def valid(r: int, c: int, ch: str) -> bool: for i in range(len(board)): if board[r][i] == ch: return False if board[i][c] == ch: return False if board[3*(r//3) + i//3][3*(c//3) + i%3] == ch: return False return True def dfs() -> bool: for i in range(len(board)): for j in range(len(board[0])): if board[i][j] != ".": continue for ch in "123456789": if not valid(i, j, ch): continue board[i][j] = ch if dfs(): return True board[i][j] = "." return False return True dfs() -
22. 括号生成
class Solution: def generateParenthesis(self, n: int) -> List[str]: ans = [] left, right = 0, 0 def dfs(idx: int, path: List[str]): nonlocal left, right if idx >= 2 * n: if left == n and right == n: ans.append("".join(path)) return # 广义剪枝 if left > n or right > n or right > left: return path.append("(") left += 1 dfs(idx + 1, path) left -= 1 path.pop() path.append(")") right += 1 dfs(idx + 1, path) right -= 1 path.pop() dfs(0, []) return ans -
17. 电话号码的字母组合
class Solution: def letterCombinations(self, digits: str) -> List[str]: if not digits: return [] keyMap = { "2": "abc", "3": "def", "4": "ghi", "5": "jkl", "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz", } ans = [] def dfs(idx: int, path: List[str]): if idx >= len(digits): ans.append("".join(path)) return digit = digits[idx] for ch in keyMap[digit]: path.append(ch) dfs(idx + 1, path) path.pop() dfs(0, []) return ans -
784. 字母大小写全排列
class Solution: def letterCasePermutation(self, s: str) -> List[str]: n = len(s) ans = [] def dfs(idx: int, path: List[str]): if idx >= n: ans.append("".join(path)) return if s[idx].isdigit(): path.append(s[idx]) dfs(idx + 1, path) path.pop() return path.append(s[idx].lower()) dfs(idx + 1, path) path.pop() path.append(s[idx].upper()) dfs(idx + 1, path) path.pop() dfs(0, []) return ans
-
剪枝
-
47. 全排列 II
class Solution: def permuteUnique(self, nums: List[int]) -> List[List[int]]: nums.sort() n = len(nums) vis = [False] * n ans = [] def dfs(idx: int, path: List[int]): if idx >= n: ans.append(path[:]) return for i in range(n): if vis[i]: continue # 重复性剪枝 if i > 0 and nums[i] == nums[i-1] and not vis[i-1]: continue vis[i] = True path.append(nums[i]) dfs(idx + 1, path) path.pop() vis[i] = False dfs(0, []) return ans -
39. 组合总和
class Solution: def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: n = len(candidates) ans = [] tmpSum = 0 def dfs(idx: int, path: List[int]): nonlocal tmpSum if idx >= n: if tmpSum == target: ans.append(path[:]) return # 广义剪枝 if tmpSum > target: return dfs(idx + 1, path) tmpSum += candidates[idx] path.append(candidates[idx]) dfs(idx, path) path.pop() tmpSum -= candidates[idx] dfs(0, []) return ans -
77. 组合
class Solution: def combine(self, n: int, k: int) -> List[List[int]]: ans = [] def dfs(idx: int, path: List[int]): if idx > n: if len(path) == k: ans.append(path[:]) return # 广义剪枝 if len(path) > k: return dfs(idx + 1, path) path.append(idx) dfs(idx + 1, path) path.pop() dfs(1, []) return ans -
473. 火柴拼正方形
class Solution: def makesquare(self, matchsticks: List[int]) -> bool: n = len(matchsticks) perimeter = sum(matchsticks) if perimeter % 4: return False possible_side = perimeter // 4 # 为了减少搜索量,需要对火柴长度从大到小进行排序 matchsticks.sort(reverse=True) edges = [0] * 4 def dfs(idx: int) -> bool: if idx >= n: return edges[0] == edges[1] == edges[2] == possible_side for i in range(4): if edges[i] + matchsticks[idx] <= possible_side: edges[i] += matchsticks[idx] if dfs(idx + 1): return True edges[i] -= matchsticks[idx] return False return dfs(0) -
40. 组合总和 II
class Solution: def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: candidates.sort() n = len(candidates) vis = [False] * n ans = [] tmpSum = 0 def dfs(idx: int, path: List[int]): nonlocal tmpSum if idx >= n: if tmpSum == target: ans.append(path[:]) return # 广义剪枝 if tmpSum > target: return # 重复性剪枝 if idx > 0 and candidates[idx] == candidates[idx-1] and not vis[idx-1]: dfs(idx + 1, path) return dfs(idx + 1, path) vis[idx] = True tmpSum += candidates[idx] path.append(candidates[idx]) dfs(idx + 1, path) path.pop() tmpSum -= candidates[idx] vis[idx] = False dfs(0, []) return ans -
78. 子集
class Solution: def subsets(self, nums: List[int]) -> List[List[int]]: n = len(nums) ans = [] def dfs(idx: int, path: List[int]): if idx >= n: ans.append(path[:]) return dfs(idx + 1, path) path.append(nums[idx]) dfs(idx + 1, path) path.pop() dfs(0, []) return ans -
90. 子集 II
class Solution: def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: n = len(nums) vis = [False] * n ans = [] nums.sort() def dfs(idx: int, path: List[int]): if idx >= n: ans.append(path[:]) return # 重复性剪枝 if idx > 0 and nums[idx] == nums[idx-1] and not vis[idx-1]: dfs(idx + 1, path) return dfs(idx + 1, path) vis[idx] = True path.append(nums[idx]) dfs(idx + 1, path) path.pop() vis[idx] = False dfs(0, []) return ans -
1593. 拆分字符串使唯一子字符串的数目最大
class Solution: def maxUniqueSplit(self, s: str) -> int: n = len(s) seen = set() ans = [] def dfs(idx: int, path: List[str]): if idx >= n: ans.append(path[:]) return for i in range(1, (n-1 - idx + 1) + 1): substr = s[idx:idx+i] if substr in seen: continue seen.add(substr) path.append(substr) dfs(idx + i, path) path.pop() seen.remove(substr) dfs(0, []) return max(map(len, ans)) -
1079. 活字印刷
class Solution: def numTilePossibilities(self, tiles: str) -> int: n = len(tiles) tiles = sorted(tiles) vis = [False] * n ans = [] def dfs(path: List[str]): ans.append("".join(path)) for i in range(n): if vis[i]: continue # 重复性剪枝 if i > 0 and tiles[i] == tiles[i-1] and not vis[i-1]: continue vis[i] = True path.append(tiles[i]) dfs(path) path.pop() vis[i] = False dfs([]) return len(ans)-1
-
二维平面上的搜索问题(Flood Fill)
-
79. 单词搜索
class Solution: def exist(self, board: List[List[str]], word: str) -> bool: m, n, wordLen = len(board), len(board[0]), len(word) vis = set() dr = [-1,1,0,0] dc = [0,0,-1,1] def dfs(idx: int, r: int, c: int) -> bool: if idx >= wordLen: return True # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return False if (r, c) in vis: return False if word[idx] != board[r][c]: return False vis.add((r, c)) for i in range(4): nr, nc = r + dr[i], c + dc[i] if dfs(idx + 1, nr, nc): return True vis.remove((r,c)) return False for i in range(m): for j in range(n): if dfs(0, i, j): return True return False -
695. 岛屿的最大面积
class Solution: def maxAreaOfIsland(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) vis = set() dr = [-1,1,0,0] dc = [0,0,-1,1] def dfs(r: int, c: int) -> int: # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return 0 if grid[r][c] != 1: return 0 if (r, c) in vis: return 0 vis.add((r, c)) area = 1 for i in range(4): nr, nc = r + dr[i], c + dc[i] area += dfs(nr, nc) return area ans = 0 for i in range(m): for j in range(n): if grid[i][j] == 1 and (i,j) not in vis: area = dfs(i, j) ans = max(ans, area) return ans -
130. 被围绕的区域
class Solution: def solve(self, board: List[List[str]]) -> None: m, n = len(board), len(board[0]) dr = [-1,1,0,0] dc = [0,0,-1,1] def dfs(r: int, c: int): # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return if board[r][c] != 'O': return board[r][c] = "A" for i in range(4): nr, nc = r + dr[i], c + dc[i] dfs(nr, nc) for i in range(m): dfs(i, 0) dfs(i, n-1) for j in range(1, n-1): dfs(0, j) dfs(m-1, j) for i in range(m): for j in range(n): if board[i][j] == "A": board[i][j] = "O" elif board[i][j] == "O": board[i][j] = "X" -
200. 岛屿数量
class Solution: def numIslands(self, grid: List[List[str]]) -> int: m, n = len(grid), len(grid[0]) vis = set() dr = [-1,1,0,0] dc = [0,0,-1,1] def dfs(r: int, c: int): # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return if grid[r][c] == "0": return if (r, c) in vis: return vis.add((r, c)) for i in range(4): nr, nc = r + dr[i], c + dc[i] dfs(nr, nc) ans = 0 for i in range(m): for j in range(n): if grid[i][j] == "1" and (i, j) not in vis: dfs(i, j) ans += 1 return ans -
417. 太平洋大西洋水流问题
class Solution: def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: m, n = len(heights), len(heights[0]) dr = [0,0,-1,1] dc = [-1,1,0,0] def dfs(r: int, c: int, arr: List[List[int]]): # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return if arr[r][c] == 1: return arr[r][c] = 1 for i in range(4): nr, nc = r + dr[i], c + dc[i] if 0 <= nr < m and 0 <= nc < n and heights[nr][nc] >= heights[r][c]: dfs(nr, nc, arr) pacific = [[0] * n for _ in range(m)] atlantic = [[0] * n for _ in range(m)] for j in range(n): dfs(0, j, pacific) dfs(m-1, j, atlantic) for i in range(m): dfs(i, 0, pacific) dfs(i, n-1, atlantic) return [[i,j] for i in range(m) for j in range(n) if pacific[i][j] and atlantic[i][j]] -
1020. 飞地的数量
class Solution: def numEnclaves(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) dr = [-1,1,0,0] dc = [0,0,-1,1] def dfs(r: int, c: int): # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return if grid[r][c] != 1: return grid[r][c] = 0 for i in range(4): nr, nc = r + dr[i], c + dc[i] dfs(nr, nc) for i in range(m): dfs(i, 0) dfs(i, n-1) for j in range(1, n-1): dfs(0, j) dfs(m-1, j) ans = 0 for i in range(m): for j in range(n): if grid[i][j] == 1: ans += 1 return ans -
1254. 统计封闭岛屿的数目
class Solution: def closedIsland(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) dr = [-1,1,0,0] dc = [0,0,-1,1] def dfs(r: int, c: int): # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return if grid[r][c] != 0: return grid[r][c] = "x" for i in range(4): nr, nc = r + dr[i], c + dc[i] dfs(nr, nc) for i in range(m): dfs(i, 0) dfs(i, n-1) for j in range(1, n-1): dfs(0, j) dfs(m-1, j) ans = 0 for i in range(m): for j in range(n): if grid[i][j] == 0: dfs(i, j) ans += 1 return ans -
1034. 边界着色
class Solution: def colorBorder(self, grid: List[List[int]], row: int, col: int, color: int) -> List[List[int]]: m, n = len(grid), len(grid[0]) vis = set() dr = [-1,1,0,0] dc = [0,0,-1,1] def isBorder(r: int, c: int, originalColor: int) -> bool: if r in [0, m-1] or c in [0, n-1]: return True for i in range(4): nr, nc = r + dr[i], c + dc[i] if (nr, nc) not in vis and grid[nr][nc] != originalColor: return True return False def dfs(r: int, c: int, originalColor: int): # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return if (r, c) in vis: return if grid[r][c] != originalColor: return vis.add((r, c)) if isBorder(r, c, originalColor): grid[r][c] = color for i in range(4): nr, nc = r + dr[i], c + dc[i] dfs(nr, nc, originalColor) dfs(row, col, grid[row][col]) return grid -
133. 克隆图
""" # 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 [] """ class Solution: def __init__(self): self.vis = {} def cloneGraph(self, node: 'Node') -> 'Node': if not node: return node if node in self.vis: return self.vis[node] clone_node = Node(node.val, []) self.vis[node] = clone_node if node.neighbors: clone_node.neighbors = [self.cloneGraph(n) for n in node.neighbors] return clone_node -
面试题13. 机器人的运动范围
class Solution: def movingCount(self, m: int, n: int, k: int) -> int: board = [[0] * n for _ in range(m)] for i in range(m): for j in range(n): total = 0 for ch in str(i): total += int(ch) for ch in str(j): total += int(ch) if total <= k: board[i][j] = 1 vis = set() dr = [-1,1,0,0] dc = [0,0,-1,1] def dfs(r: int, c: int) -> int: # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return 0 if board[r][c] == 0: return 0 if (r, c) in vis: return 0 vis.add((r, c)) count = 1 for i in range(4): nr, nc = r + dr[i], c + dc[i] count += dfs(nr, nc) return count return dfs(0, 0) -
529. 扫雷游戏
class Solution: def updateBoard(self, board: List[List[str]], click: List[int]) -> List[List[str]]: i, j = click[0], click[1] if board[i][j] == 'M': board[i][j] = 'X' return board m, n = len(board), len(board[0]) dr = [-1,-1,-1,0,0,1,1,1] dc = [-1,0,1,-1,1,-1,0,1] def dfs(r: int, c: int): # 广义剪枝 if not (0 <= r < m) or not (0 <= c < n): return if board[r][c] != 'E': return mineCnt = 0 for i in range(8): nr, nc = r + dr[i], c + dc[i] if 0 <= nr < m and 0 <= nc < n and board[nr][nc] == 'M': mineCnt += 1 if mineCnt > 0: board[r][c] = str(mineCnt) return board[r][c] = 'B' for i in range(8): nr, nc = r + dr[i], c + dc[i] dfs(nr, nc) dfs(i, j) return board
-
-
动态规划与深度优先遍历思想的结合
-
543. 二叉树的直径
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: ans = 0 # dfs返回必定经过node且node为端点的最长路径长度 # 利用「无后效性」的思想(固定住一些状态,或者对当前维度进行升维) def dfs(node: Optional[TreeNode]) -> int: if not node: return 0 # 正体递归 leftRet = dfs(node.left) rightRet = dfs(node.right) # 后处理 nonlocal ans ans = max(ans, leftRet + rightRet + 1) return max(leftRet, rightRet) + 1 dfs(root) return ans - 1 -
124. 二叉树中的最大路径和
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def maxPathSum(self, root: Optional[TreeNode]) -> int: ans = -inf # dfs返回必定经过node且node为端点的最大路径和 def dfs(node: Optional[TreeNode]) -> int: if not node: return 0 # 正体递归 leftRet = max(dfs(node.left), 0) rightRet = max(dfs(node.right), 0) # 后处理 nonlocal ans ans = max(ans, leftRet + rightRet + node.val) return max(leftRet, rightRet) + node.val dfs(root) return ans -
298. 二叉树最长连续序列
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def longestConsecutive(self, root: Optional[TreeNode]) -> int: ans = 0 # dfs返回必定经过node且node为端点的最长连续序列的长度 def dfs(node: Optional[TreeNode]) -> int: if not node: return 0 # 正体递归 leftRet = dfs(node.left) rightRet = dfs(node.right) # 后处理 if node.left: if node.left.val != node.val + 1: leftRet = 0 if node.right: if node.right.val != node.val + 1: rightRet = 0 nonlocal ans ans = max(ans, max(leftRet, rightRet) + 1) return max(leftRet, rightRet) + 1 dfs(root) return ans -
549. 二叉树中最长的连续序列
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def longestConsecutive(self, root: Optional[TreeNode]) -> int: ans = 0 # dfs返回必定经过node且node为端点的最长递增连续序列和最长递减序列的长度 def dfs(node: Optional[TreeNode]) -> Tuple[int]: if not node: return (0, 0) # 正体递归 leftInc, leftDcr = dfs(node.left) rightInc, rightDcr = dfs(node.right) # 后处理 inc, dcr = 1, 1 if node.left: if node.left.val == node.val + 1: inc = max(inc, leftInc + 1) elif node.left.val == node.val - 1: dcr = max(dcr, leftDcr + 1) if node.right: if node.right.val == node.val + 1: inc = max(inc, rightInc + 1) elif node.right.val == node.val - 1: dcr = max(dcr, rightDcr + 1) nonlocal ans ans = max(ans, inc + dcr - 1) return (inc, dcr) dfs(root) return ans -
687. 最长同值路径
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def longestUnivaluePath(self, root: Optional[TreeNode]) -> int: ans = 0 # dfs返回必定经过node且node为端点的最长同值路径 def dfs(node: Optional[TreeNode]) -> int: if not node: return 0 # 正体递归 leftRet = dfs(node.left) rightRet = dfs(node.right) # 后处理 if node.left: if node.left.val != node.val: leftRet = 0 if node.right: if node.right.val != node.val: rightRet = 0 nonlocal ans ans = max(ans, leftRet + rightRet + 1) return max(leftRet, rightRet) + 1 if not root: return 0 dfs(root) return ans - 1 -
1372. 二叉树中的最长交错路径
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def longestZigZag(self, root: TreeNode) -> int: ans = 0 # dfs返回必定经过node且node为端点的最长左-右-左和最长右-左-右路径的长度lrl和rlr def dfs(node: TreeNode) -> Tuple[int]: if not node: return (0, 0) # 正体递归,lrl: left - right - left,rlr: right - left - right leftlrl, leftrlr = dfs(node.left) rightlrl, rightrlr = dfs(node.right) # 后处理 lrl, rlr = 1, 1 if node.left: lrl = 1 + leftrlr if node.right: rlr = 1 + rightlrl nonlocal ans ans = max(ans, max(lrl, rlr)) return (lrl, rlr) dfs(root) return ans - 1 -
968. 监控二叉树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def minCameraCover(self, root: TreeNode) -> int: # a: node 必须放置摄像头的情况下,覆盖整棵树需要的摄像头数目 # b: 覆盖整棵树需要的摄像头数目,无论 root 是否放置摄像头 # c: 覆盖整棵树需要的摄像头数目,无论 root 本身是否被监控到 def dfs(node: TreeNode) -> List[int]: if not node: return [math.inf, 0, 0] la, lb, lc = dfs(node.left) ra, rb, rc = dfs(node.right) a = lc + rc + 1 b = min(a, la + rb, ra + lb) c = min(a, lb + rb) return [a, b, c] a, b, c = dfs(root) return b -
865. 具有所有最深节点的最小子树
# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def subtreeWithAllDeepest(self, root: TreeNode) -> TreeNode: maxDepth = 0 deepestNodes = set() # dfs用于获得最深节点的全体集合 def dfs(node: TreeNode, depth: int): if not node: return if not node.left and not node.right: nonlocal maxDepth if depth > maxDepth: maxDepth = depth deepestNodes.clear() deepestNodes.add(node) elif depth == maxDepth: deepestNodes.add(node) return dfs(node.left, depth + 1) dfs(node.right, depth + 1) dfs(root, 0) ans = None # dfs1返回node所在的子树中是否存在最深节点,处理类似于最近公共祖先 def dfs1(node: TreeNode) -> bool: if not node: return False nonlocal ans if node in deepestNodes: ans = node return True # 正体递归 leftRet = dfs1(node.left) rightRet = dfs1(node.right) # 后处理 if leftRet and rightRet: ans = node return True elif leftRet or rightRet: return True return False dfs1(root) return ans -
1102. 得分最高的路径
class Solution: def maximumMinimumPath(self, grid: List[List[int]]) -> int: m, n = len(grid), len(grid[0]) dr = [-1,1,0,0] dc = [0,0,-1,1] # dfs返回采用最小值为mid的得分路径是否可以走通 def dfs(r: int, c: int, mid: int) -> bool: if r == m - 1 and c == n - 1: return True # 广义剪枝 if (r, c) in vis: return False vis.add((r , c)) for i in range(4): nr, nc = r + dr[i], c + dc[i] if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] >= mid: if dfs(nr, nc, mid): return True return False # 深度优先搜索 + 二分 lo, hi = 0, min(grid[0][0], grid[m-1][n-1]) while lo < hi: mi = (lo + hi) // 2 vis = set() if dfs(0, 0, mi): if lo != mi: lo = mi else: vis = set() if dfs(0, 0, hi): return hi else: return lo else: hi = mi - 1 return lo -
1631. 最小体力消耗路径
class Solution: def minimumEffortPath(self, heights: List[List[int]]) -> int: m, n = len(heights), len(heights[0]) dr = [-1,1,0,0] dc = [0,0,-1,1] # dfs返回采用最小值为mid的体力路径是否可以走通 def dfs(r: int, c: int, mid: int) -> bool: if r == m - 1 and c == n - 1: return True # 广义剪枝 if (r, c) in vis: return False vis.add((r , c)) for i in range(4): nr, nc = r + dr[i], c + dc[i] if 0 <= nr < m and 0 <= nc < n and abs(heights[nr][nc]-heights[r][c]) <= mid: if dfs(nr, nc, mid): return True return False # 深度优先搜索 + 二分 lo, hi = 0, 10**6 while lo < hi: mi = (lo + hi) // 2 vis = set() if dfs(0, 0, mi): hi = mi else: lo = mi + 1 return lo -
778. 水位上升的泳池中游泳
class Solution: def swimInWater(self, grid: List[List[int]]) -> int: n = len(grid) dr = [-1,1,0,0] dc = [0,0,-1,1] # dfs返回采用最小值为mid的体力路径是否可以走通 def dfs(r: int, c: int, mid: int) -> bool: # 能到达最后一个格子,说明该水位可以成功 if r == n - 1 and c == n - 1: return True # 广义剪枝 if (r, c) in vis: return False vis.add((r, c)) for i in range(4): nr, nc = r + dr[i], c + dc[i] if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] <= mid: if dfs(nr, nc, mid): return True return False # 深度优先搜索 + 二分 lo, hi = grid[0][0], n**2 while lo < hi: mi = (lo + hi) // 2 vis = set() if dfs(0, 0, mi): hi = mi else: lo = mi + 1 return lo -
403. 青蛙过河
class Solution: def canCross(self, stones: List[int]) -> bool: n = len(stones) stone2index = {} for i, v in enumerate(stones): stone2index[v] = i @cache def dfs(idx: int, k: int) -> bool: if idx >= n - 1: return True for nk in [k-1, k, k+1]: if nk > 0 and stones[idx] + nk in stone2index: next_idx = stone2index[stones[idx] + nk] if dfs(next_idx, nk): return True return False return dfs(0, 0)
-