回溯 - 子集、组合、排列、岛屿
result = [] def backtrack(路径, 选择列表): if 满足结束条件:
result.add(路径) return for 选择 in 选择列表: 做选择 backtrack(路径,
选择列表) 撤销选择
39.组合总和
40.
组合总和 II
46.
全排列
47.
全排列 II
78. 子集
90. 子集
II
给定一个整数数组 nums
,数组中的元素
互不相同 。返回该数组所有可能的子集(幂集)。
1 2 输入:nums = [1 ,2 ,3 ] 输出:[[],[1 ],[2 ],[1 ,2 ],[3 ],[1 ,3 ],[2 ,3 ],[1 ,2 ,3 ]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution : def subsets (self, nums: List [int ] ) -> List [List [int ]]: n = len (nums) ans = [] def backtrace (idx, tmp ): ans.append(tmp[:]) for j in range (idx, n): tmp.append(nums[j]) backtrace(j + 1 , tmp) tmp.pop() backtrace(0 , []) return ans def subsets (self, nums: List [int ] ) -> List [List [int ]]: ans = [] for i in range (len (nums) + 1 ): for tmp in itertools.combinations(nums, i): ans.append(tmp) return ans
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word
存在于网格中,返回 true ;否则,返回 false
。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例如,在下面的 3×4 的矩阵中包含单词
"ABCCED"(单词中的字母已标出)。
img
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution : def exist (self, board: List [List [str ]], word: str ) -> bool : m = len (board) n = len (board[0 ]) def dfs (x, y, index ): if index == len (word): return True for dx, dy in ((-1 , 0 ), (1 , 0 ), (0 , -1 ), (0 , 1 )): nx, ny = x + dx, y + dy if 0 <= nx < m and 0 <= ny < n and board[nx][ny] == word[index]: board[nx][ny] = '/' if dfs(nx, ny, index + 1 ): return True board[nx][ny] = word[index] return False for i in range (m): for j in range (n): if board[i][j] == word[0 ]: board[i][j] = '/' if dfs(i, j, 1 ): return True board[i][j] = word[0 ] return False
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1]
。一个机器人从坐标 [0, 0]
的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子 。例如,当k为18时,机器人能够进入方格
[35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35,
38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Solution : def movingCount (self, m: int , n: int , k: int ) -> int : def sum_ (x, y ): return x%10 + x//10 + y%10 + y//10 que = collections.deque() que.append((0 , 0 )) s = set () s.add((0 , 0 )) while que: x, y = que.popleft() for i, j in [(x + 1 , y), (x, y + 1 )]: if 0 <= i < m and 0 <= j < n and (i, j) not in s and sum_(i,j) <= k: que.append((i, j)) s.add((i, j)) return len (s) def movingCount (self, m: int , n: int , k: int ) -> int : def dfs (i: int , j: int ) -> int : if i >= m or j >= n or sum_(i, j) > k or f[i][j] == 1 : return 0 f[i][j] = 1 return 1 + dfs(i + 1 , j) + dfs(i, j + 1 ) def sum_ (x, y ): return x%10 + x//10 + y%10 + y//10 f = [[0 for i in range (n)] for j in range (m)] return dfs(0 , 0 ) def movingCount (self, m: int , n: int , k: int ) -> int : def sumGrid (x, y ): return x%10 + x//10 + y%10 + y//10 visited = set () def backtrace (x, y ): visited.add((x, y)) for i, j in [(x-1 , y), (x, y-1 ), (x, y+1 ), (x+1 , y)]: if 0 <= i < m and 0 <= j < n and (i, j) not in visited and sumGrid(i, j) <= k: backtrace(i, j) backtrace(0 , 0 ) return len (visited)
给你二叉树的根节点 root 和一个整数目标和 targetSum
,找出==所有==从根节点到叶子节点
路径总和等于给定目标和的路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution : def pathSum (self, root: TreeNode, target: int ) -> List [List [int ]]: def backtrace (root, tmp, target ): if not root: return if not root.left and not root.right and target == root.val: ans.append(tmp[:] + [root.val]) return tmp.append(root.val) backtrace(root.left, tmp, target - root.val) backtrace(root.right, tmp, target - root.val) tmp.pop() ans = [] backtrace(root, [], target) return ans
https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/solution/dai-ma-sui-xiang-lu-jian-zhi-offer-38-zi-gwt6/
输入一个字符串,打印出该字符串中字符的所有排列 。
还要强调的是==去重一定要对元素经行排序==,这样我们才方便通过相邻的节点来判断是否重复使用了 。
组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution : def permutation (self, s: str ) -> List [str ]: return list (set ("" .join(t) for t in itertools.permutations(s))) def permuteUnique (self, nums: List [int ] ) -> List [List [int ]]: if not nums: return [] res = [] used = set () def backtracking (path ): if len (path) == len (nums): res.append(path[:]) return for i in range (len (nums)): if i not in used: if i>0 and nums[i] == nums[i-1 ] and i-1 not in used: continue used.add(i) path.append(nums[i]) backtracking(path) path.pop() used.remove(i) nums.sort() backtracking([]) return res
给定一个无重复元素的正整数数组 candidates 和一个正整数
target ,找出 candidates 中所有可以使数字和为目标数 target
的唯一组合。candidates
中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是不同的 。
输入: candidates = [2,3,6,7], target = 7 输出: [[7],[2,2,3]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution : def combinationSum (self, candidates: List [int ], target: int ) -> List [List [int ]]: ans = [] path = [] candidates.sort() def backtracking (index,path,target ): if index >= len (candidates) or target < 0 : return if target == 0 : ans.append(path[:]) return for i in range (index,len (candidates)): path.append(candidates[i]) backtracking(i,path,target-candidates[i]) path.pop() backtracking(0 ,[],target) return ans
给定一个可能有重复数字的整数数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用一次,解集不能包含重复的组合。
1 2 3 4 5 6 7 8 输入: candidates = [10 ,1 ,2 ,7 ,6 ,1 ,5 ], target = 8 , 输出: [ [1 ,1 ,6 ], [1 ,2 ,5 ], [1 ,7 ], [2 ,6 ] ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution : def combinationSum2 (self, candidates: List [int ], target: int ) -> List [List [int ]]: ans, n = [], len (candidates) candidates.sort() def backtrace (idx, tmp, target ): if idx > n or target < 0 : return if target == 0 : ans.append(tmp[:]) return for i in range (idx, n): if i > idx and candidates[i] == candidates[i - 1 ]: continue tmp.append(candidates[i]) backtrace(i + 1 , tmp, target - candidates[i]) tmp.pop() backtrace(0 , [], target) return ans
给定一个不含重复数字的整数数组 nums
,返回其
所有可能的全排列 。可以 按任意顺序
返回答案。
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution : def permute (self, nums: List [int ] ) -> List [List [int ]]: ans, n = [], len (nums) visited = set () def backtrace (idx, tmp ): if len (tmp) == n: ans.append(tmp[:]) return for i in range (n): if i not in visited: visited.add(i) tmp.append(nums[i]) backtrace(i + 1 , tmp) tmp.pop() visited.remove(i) backtrace(0 ,[]) return ans
给定一个可包含重复数字的整数集合 nums
,按任意顺序 返回它所有不重复的全排列。
输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution : def permuteUnique (self, nums: List [int ] ) -> List [List [int ]]: n = len (nums) ans = [] vis = set () def backtrace (tmp ): if len (tmp) == n: ans.append(tmp[:]) return for i in range (n): if i in vis: continue if i > 0 and nums[i] == nums[i - 1 ] and i - 1 not in vis: continue vis.add(i) backtrace(tmp + [nums[i]]) vis.remove(i) nums.sort() backtrace([]) return ans
数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且
有效的 括号组合。
输入:n = 3 输出:["((()))","(()())","(())()","()(())","()()()"]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution : def generateParenthesis (self, n: int ) -> List [str ]: ans = [] def backtrace (left, right, tmp ): if left > right: return if left < 0 or right < 0 : return if left == 0 and right == 0 : ans.append("" .join(tmp[:])) return tmp.append('(' ) backtrace(left - 1 , right, tmp) tmp.pop() tmp.append(')' ) backtrace(left, right - 1 , tmp) tmp.pop() backtrace(n, n, []) return ans
给定一个字符串 s ,请将 s 分割成一些子串,使每个子串都是 回文串
,返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。
输入:s = "google"
输出:[["g","o","o","g","l","e"],["g","oo","g","l","e"],["goog","l","e"]]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution : def partition (self, s: str ) -> List [List [str ]]: n = len (s) dp = [[False ] * n for _ in range (n)] for i in range (n): dp[i][i] = True for i in range (n - 1 , -1 , -1 ): for j in range (i + 1 , n): if s[i] == s[j]: if j - i <= 1 or dp[i + 1 ][j - 1 ]: dp[i][j] = True ans = [] def backtrace (idx, tmp ): if idx == n: ans.append(tmp[:]) return for i in range (idx, n): if dp[idx][i]: tmp.append(s[idx: i + 1 ]) backtrace(i + 1 , tmp) tmp.pop() backtrace(0 , []) return ans
有效 IP 地址 正好由四个整数(每个整数位于
0
到 255
之间组成,且不能含有前导
0
),整数之间用 '.'
分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是
"0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP
地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP
地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.'
来形成。 你 不能 重新排序或删除 s 中的任何数字。你可以按 任何
顺序返回答案。
输入:s = "25525511135" 输出:["255.255.11.135","255.255.111.35"]
如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯;
由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution : def restoreIpAddresses (self, s: str ) -> List [str ]: SEG_COUNT = 4 ans = list () segments = [0 ] * SEG_COUNT def dfs (segId: int , segStart: int ): if segId == SEG_COUNT: if segStart == len (s): ipAddr = "." .join(str (seg) for seg in segments) ans.append(ipAddr) return if segStart == len (s): return if s[segStart] == "0" : segments[segId] = 0 dfs(segId + 1 , segStart + 1 ) addr = 0 for segEnd in range (segStart, len (s)): addr = addr * 10 + (ord (s[segEnd]) - ord ("0" )) if 0 < addr <= 0xFF : segments[segId] = addr dfs(segId + 1 , segEnd + 1 ) else : break dfs(0 , 0 ) return ans