排列&组合&子集

模板

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]
]

思路

使用深度优先遍历

image-20200512135443652

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
给定两个整数 nk,返回 1 ... n 中所有可能的 k 个数的组合。

示例:

输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

思路

回溯

遍历从in,添加到路径;递归从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
posted @ 2020-05-12 15:56  鱼与鱼  阅读(184)  评论(0编辑  收藏  举报