排列&组合&子集
模板
result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择
排列问题,讲究顺序(即 [2, 2, 3] 与 [2, 3, 2] 视为不同列表时),需要记录哪些数字已经使用过,此时用 used 数组;
组合问题,不讲究顺序(即 [2, 2, 3] 与 [2, 3, 2] 视为相同列表时),需要按照某种顺序搜索,此时使用 begin 变量。
全排列
LeetCode入口👉👉👉No.46
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]
思路
使用深度优先遍历

coding
python库简洁实现
#--python
from itertools import permutations
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        return list(permutations(nums))
深度优先遍历 回溯
#--python
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def dfs(nums,path,used):
            if len(path) == size:
                res.append(path[:])
                return 
            for i in range(len(nums)):
                if not used[i]:
                    used[i] = True
                    path.append(nums[i])
                    dfs(nums,path,used)
                    path.pop()
                    used[i] = False
                    
        size = len(nums)
        res = []
        used = [False for _ in range(len(nums))]
        dfs(nums,[],used)
        return res
全排列2
LeetCode入口👉👉👉No.47
给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]
思路
- 
暴力法(后剪枝)
首先 生成全排列,然后对结果进行去重
 - 
在生成过程中去重(预剪枝)
什么情况下会出现重复?
当一个组合的起始数字和上一个数字相同,并且上一个数字没有在路径中出现
例如,红框内组合的起始数字
1与之前组合的起始数字1相同,并且在新组合中1没有被加入路径即
nums[i] == nums[i - 1] && !used[i - 1]![image-20200512142757681]()
 
coding
暴力法 后剪枝
#--python
class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def backtrack(nums,path,used):
            if len(path)==size:
                res.append(path[:])
                return
            for i in range(len(nums)):
                if not used[i]:
                    used[i] = True
                    path.append(nums[i])
                    backtrack(nums,path,used)
                    path.pop()
                    used[i] = False
                    
        size = len(nums)
        res = []
        used = [False for _ in range(len(nums))]
        backtrack(nums,[],used)
        #去重
        unique = []
        for p in res:
            if p not in unique:
                unique.append(p)
        return unique
在生成过程中去重 (需要先对数组排序)
#--python
class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def backtrack(nums,path,used):
            if len(path)==size:
                res.append(path[:])
                return
            for i in range(len(nums)):
                if not used[i]:
                	#去重
                    if (i>0 and nums[i]==nums[i-1] and not used[i-1]):
                        continue
                    used[i] = True
                    path.append(nums[i])
                    backtrack(nums,path,used)
                    path.pop()
                    used[i] = False
                    
        size = len(nums)
        res = []
        used = [False for _ in range(len(nums))]
        nums.sort()
        backtrack(nums,[],used)
        return res
组合
LeetCode入口👉👉👉No.77
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]
思路
回溯
遍历从i到n,添加到路径;递归从i+1开始添加
coding
python库简洁实现
#--python
from itertools import combinations
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        return list(combinations(list(range(1,n+1)), k))
回溯
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        def backtrack(start,path):
            if len(path) == k:
                res.append(path[:])
                return
            for i in range(start,len(nums)):
                path.append(nums[i])
                backtrack(i+1,path)
                path.pop()
                    
        res = []
        nums = list(range(1,n+1))
        # used = [False]*len(nums)
        backtrack(0,[])
        return res
子集
LeetCode入口👉👉👉No.78
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
思路
回溯:生成所有给定长度子集
递归:每一次向已生成子集添加新元素
coding
递归
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = [[]]
        for num in nums:
            res+=[[num]+cur for cur in res]
        return res
回溯
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        def backtrack(start,path):
            if len(path) == k:
                res.append(path[:])
            for i in range(start,len(nums)):
                path.append(nums[i])
                backtrack(i+1,path)
                path.pop()
        res = []
        path = []
        for k in range(len(nums)+1):
            backtrack(0,path)
        return res
                    
                

                
            
        
浙公网安备 33010602011771号