回溯算法 发表于 2023-07-13 | 更新于 2023-07-14
| 阅读量:
77.组合 给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
1 2 3 4 5 6 7 8 9 10 输入:n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
示例 2:
1 2 输入:n = 1, k = 1 输出:[[1]]
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution : def combine (self, n: int , k: int ) -> List [List [int ]]: res=[] path=[] def backtrack (n,k,startIndex ): if len (path) == k: res.append(path[:]) return for i in range (startIndex,n-(k-len (path))+2 ): path.append(i) backtrack(n,k,i+1 ) path.pop() backtrack(n,k,1 ) return res
216.组合总和 III 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例 1:
1 2 3 4 5 输入: k = 3, n = 7 输出: [[1,2,4]] 解释: 1 + 2 + 4 = 7 没有其他符合的组合了。
示例 2:
1 2 3 4 5 6 7 输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]] 解释: 1 + 2 + 6 = 9 1 + 3 + 5 = 9 2 + 3 + 4 = 9 没有其他符合的组合了。
示例 3:
1 2 3 4 输入: k = 4, n = 1 输出: [] 解释: 不存在有效的组合。 在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution : def combinationSum3 (self,k: int , n: int ): result=[] path=[] def fn (n,k,startindex ): if len (path)==k: if sum (path)==n: result.append(path[:]) return for i in range (startindex,9 -(k-len (path))+2 ): path.append(i) fn(n,k,i+1 ) path.pop() fn(n,k,1 ) return result
17.电话号码的字母组合 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
1 2 输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
示例 3:
1 2 输入:digits = "2" 输出:["a","b","c"]
代码:
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 class Solution : def __init__ (self ): self.answers=[] self.answer='' self.letter_map = { '2' : 'abc' , '3' : 'def' , '4' : 'ghi' , '5' : 'jkl' , '6' : 'mno' , '7' : 'pqrs' , '8' : 'tuv' , '9' : 'wxyz' } def letterCombinations (self, digits ): self.answers.clear() if not digits: return [] self.backtracking(digits, 0 ) return self.answers def backtracking (self, digits: str , index: int ) -> None : if index == len (digits): self.answers.append(self.answer) return letters: str = self.letter_map[digits[index]] for letter in letters: self.answer += letter self.backtracking(digits, index + 1 ) self.answer = self.answer[:-1 ]
39.组合总和 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
1 2 3 4 5 6 输入:candidates = [2,3,6,7], target = 7 输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。
示例 2:
1 2 输入: candidates = [2,3,5], target = 8 输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
1 2 输入: candidates = [2], target = 1 输出: []
代码:
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 class Solution : def __init__ (self ): self.path = [] self.paths = [] def combinationSum (self, candidates, target ) : ''' 因为本题没有组合数量限制,所以只要元素总和大于target就算结束 ''' self.path.clear() self.paths.clear() self.backtracking(candidates, target, 0 , 0 ) return self.paths def backtracking (self, candidates, target, sum_, start_index ) : if sum_==target: self.paths.append(self.path[:]) return if sum_>target: return for x in range (start_index,len (candidates)): self.path.append(candidates[x]) sum_+=candidates[x] self.backtracking(candidates,target,sum_,x) self.path.pop() sum_-=candidates[x]
40.组合总和 II 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意: 解集不能包含重复的组合。
示例 1:
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] ]
示例 2:
1 2 3 4 5 6 输入: candidates = [2,5,2,1,2], target = 5, 输出: [ [1,2,2], [5] ]
代码:
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 class Solution : def __init__ (self ): self.paths = [] self.path = [] def combinationSum2 (self, candidates: List [int ], target: int ) -> List [List [int ]]: ''' 类似于求三数之和,求四数之和,为了避免重复组合,需要提前进行数组排序 ''' self.paths.clear() self.path.clear() candidates.sort() self.backtracking(candidates, target, 0 , 0 ) return self.paths def backtracking (self, candidates: List [int ], target: int , sum_: int , start_index: int ) -> None : if sum_ == target: self.paths.append(self.path[:]) return for i in range (start_index, len (candidates)): if sum_ + candidates[i] > target: return if i > start_index and candidates[i] == candidates[i-1 ]: continue sum_ += candidates[i] self.path.append(candidates[i]) self.backtracking(candidates, target, sum_, i+1 ) self.path.pop() sum_ -= candidates[i]
131.分割回文串 给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
示例 1:
1 2 输入:s = "aab" 输出:[["a","a","b"],["aa","b"]]
示例 2:
代码:
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 class Solution : def __init__ (self ): self.paths = [] self.path = [] def partition (self, s: str ): ''' 递归用于纵向遍历 for循环用于横向遍历 当切割线迭代至字符串末尾,说明找到一种方法 类似组合问题,为了不重复切割同一位置,需要start_index来做标记下一轮递归的起始位置(切割线) ''' self.path.clear() self.paths.clear() self.backtracking(s, 0 ) return self.paths def backtracking (self, s: str , start_index: int ) -> None : if start_index >= len (s): self.paths.append(self.path[:]) return for i in range (start_index, len (s)): temp = s[start_index:i + 1 ] if temp == temp[::-1 ]: self.path.append(temp) self.backtracking(s, i + 1 ) self.path.pop() else : continue
93.复原 IP 地址 有效 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 中的任何数字。你可以按 任何 顺序返回答案。
示例 1:
1 2 输入:s = "25525511135" 输出:["255.255.11.135","255.255.111.35"]
示例 2:
1 2 输入:s = "0000" 输出:["0.0.0.0"]
示例 3:
1 2 输入:s = "101023" 输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
代码:
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 class Solution : def restoreIpAddresses (self, s: str ) -> List [str ]: result = [] self.backtracking(s, 0 , 0 , "" , result) return result def backtracking (self, s, start_index, point_num, current, result ): if point_num == 3 : if self.is_valid(s, start_index, len (s) - 1 ): current += s[start_index:] result.append(current) return for i in range (start_index, len (s)): if self.is_valid(s, start_index, i): sub = s[start_index:i + 1 ] self.backtracking(s, i + 1 , point_num + 1 , current + sub + '.' , result) else : break def is_valid (self, s, start, end ): if start > end: return False if s[start] == '0' and start != end: return False num = 0 for i in range (start, end + 1 ): if not s[i].isdigit(): return False num = num * 10 + int (s[i]) if num > 255 : return False return True
78.子集 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
1 2 输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
1 2 输入:nums = [0] 输出:[[],[0]]
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution : def subsets (self, nums ): result = [] path = [] self.backtracking(nums, 0 , path, result) return result def backtracking (self, nums, startIndex, path, result ): result.append(path[:]) for i in range (startIndex, len (nums)): path.append(nums[i]) self.backtracking(nums, i + 1 , path, result) path.pop()
90.子集 II 给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
1 2 输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
1 2 输入:nums = [0] 输出:[[],[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 class Solution : def __init__ (self ): self.paths = [] self.path = [] def subsetsWithDup (self, nums: List [int ] ) -> List [List [int ]]: nums.sort() self.backtracking(nums, 0 ) return self.paths def backtracking (self, nums: List [int ], start_index: int ) -> None : self.paths.append(self.path[:]) if start_index == len (nums): return for i in range (start_index, len (nums)): if i > start_index and nums[i] == nums[i-1 ]: continue self.path.append(nums[i]) self.backtracking(nums, i+1 ) self.path.pop()
491.递增子序列 给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
示例 1:
1 2 输入:nums = [4,6,7,7] 输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]
示例 2:
1 2 输入:nums = [4,4,3,2,1] 输出:[[4,4]]
代码:
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 class Solution : def __init__ (self ): self.paths = [] self.path = [] def findSubsequences (self, nums: List [int ] ) -> List [List [int ]]: ''' 本题求自增子序列,所以不能改变原数组顺序 ''' self.backtracking(nums, 0 ) return self.paths def backtracking (self, nums: List [int ], start_index: int ): if len (self.path) >= 2 : self.paths.append(self.path[:]) if start_index == len (nums): return usage_list = set () for i in range (start_index, len (nums)): if (self.path and nums[i] < self.path[-1 ]) or nums[i] in usage_list: continue usage_list.add(nums[i]) self.path.append(nums[i]) self.backtracking(nums, i+1 ) self.path.pop()
46.全排列 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
1 2 输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
1 2 输入:nums = [0,1] 输出:[[0,1],[1,0]]
示例 3:
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Solution : def permute (self, nums ): result = [] self.backtracking(nums, [], [False ] * len (nums), result) return result def backtracking (self, nums, path, used, result ): if len (path) == len (nums): result.append(path[:]) return for i in range (len (nums)): if used[i]: continue used[i] = True path.append(nums[i]) self.backtracking(nums, path, used, result) path.pop() used[i] = False
47.全排列 II 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
1 2 3 4 5 输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
示例 2:
1 2 输入: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 20 class Solution : def permuteUnique (self, nums ): nums.sort() result = [] self.backtracking(nums, [], [False ] * len (nums), result) return result def backtracking (self, nums, path, used, result ): if len (path) == len (nums): result.append(path[:]) return for i in range (len (nums)): if (i > 0 and nums[i] == nums[i - 1 ] and not used[i - 1 ]) or used[i]: continue used[i] = True path.append(nums[i]) self.backtracking(nums, path, used, result) path.pop() used[i] = False