目录

  • 一、无重复元素不可复选
  • 78.子集
  • 77. 组合
  • 46. 全排列
  • 二、有重复元素不可复选
  • 90. 子集 II
  • 40.组合总和 II
  • 47. 全排列 II
  • 三、无重复元素可复选(子集/组合)
  • 39. 组合总和


一、无重复元素不可复选

78.子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

  • 方法一
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        def backtrack(start):
            res.append(path[:])
            for i in range(start, n):
                path.append(nums[i])
                # 递归遍历下一层回溯树
                # 避免重复使用元素,参数i加1
                backtrack(i+1)
                path.pop()
                
        res, n = [], len(nums)
        # 记录路径
        path = []
        backtrack(0)
        return res
  • 方法二
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        def dfs(index, path):
            # base case
            if index == n:
                res.append(path)
                return
            # 不添加nums[index]
            dfs(index+1, path)
            # 添加nums[index]
            dfs(index+1, path + [nums[index]])
        n, res = len(nums), []
        dfs(0, [])
        return res

77. 组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

  • 方法一
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        def backtrack(start):
            # base case
            if len(path) == k:
                res.append(path[:])
                return
            for i in range(start, n):
                path.append(i+1)
                backtrack(i+1)
                path.pop() 
        
        res = []
        path = []
        backtrack(0)
        return res
  • 方法二
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        def dfs(i, path):
            if i == n:
                if len(path) == k:
                    res.append(path)
                return           
            dfs(i+1, path)
            dfs(i+1, path+[nums[i]])
        res = []
        nums = list(range(1,n+1))
        dfs(0, [])
        return res

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def backtrack(path):
            if len(path) == n:
                res.append(path[:])
                return
            for i in nums:
                if i in path:
                    continue
                path.append(i)
                backtrack(path)
                path.pop()
        res, n = [], len(nums)
        backtrack([])
        return res

二、有重复元素不可复选

90. 子集 II

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        def backtrack(index):
            res.append(path[:])
            for i in range(index, n):
                if i>index and nums[i] == nums[i-1]:
                    continue
                path.append(nums[i])
                backtrack(i+1)
                path.pop()
        nums.sort()
        res, n = [], len(nums)
        path = []
        backtrack(0)
        return res

40.组合总和 II

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次

**注意:**解集不能包含重复的组合。

class Solution:
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(start):
            # base case,达到目标和,找到符合条件的组合
            if self.pathSum == target:
                res.append(path[:])
                return 
            # base case,超过目标和,直接结束
            if self.pathSum > target:
                return
            for i in range(start, n):
                # 剪枝,值相同的树枝,只遍历第一条
                if i > start and candidates[i] == candidates[i-1]:
                    continue
                path.append(candidates[i])
                self.pathSum += candidates[i]
                backtrack(i+1)
                self.pathSum -= path.pop()
        # 先排序,让相同的元素靠在一起
        candidates.sort()
        res, n = [], len(candidates)
        # 记录 path 和 path 中的元素之和
        path, self.pathSum = [], 0
        backtrack(0)
        return res

47. 全排列 II

给定一个可包含重复数字的序列 nums按任意顺序 返回所有不重复的全排列。

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def backtrack(path):
            if len(path) == n:
                res.append(path[:])
                return
            for i in range(n):
                if used[i] == True:
                    continue
                # 剪枝,同时保证相同元素在排列中的相对位置保持不变
                # 注意used[i - 1] == False这个条件
                if i > 0 and nums[i] == nums[i-1] and used[i - 1] == False:
                    continue
                
                path.append(nums[i])
                used[i] = True
                backtrack(path)
                used[i] = False
                path.pop()
        # 先排序,让相同的元素靠在一起       
        nums.sort()
        res, n = [], len(nums)
        # 记录是否使用过该数字
        used = [False]*n
        backtrack([])
        return res

对比之前的标准全排列解法代码,这段解法代码只有两处不同:

1、对 nums 进行了排序。

2、添加了一句额外的剪枝逻辑。


三、无重复元素可复选(子集/组合)

39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        def backtrack(start):
            # base case,找到目标和,记录结果
            if self.pathSum == target:
                res.append(path[:])
                return
            # base case,超过目标和,停止向下遍历
            if self.pathSum > target:
                return
            
            for i in range(start, n):
                path.append(candidates[i])
                self.pathSum += candidates[i]
                # 递归遍历下一层回溯树, 同一元素可重复使用,参数填入i
                # 可重复选与不可重复选,只有参数ℹ️那里变化
                backtrack(i)
                self.pathSum -= candidates[i]
                path.pop()
        
        res, n = [], len(candidates)
        path, self.pathSum = [], 0
        backtrack(0)
        return res