【leetcode】回溯法:组合、排列

目录

77. 组合

46. 全排列

47. 全排列 II

 39. 组合总和 

40. 组合总和 II

216. 组合总和 III

78. 子集

90. 子集 II


77. 组合

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

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

这道题是比较简单的回溯法题目,这一题的解答模板就是回溯问题的基本模板

class Solution:
    def combine(self, n, k):
        output = []
        def huisu(lists,start):
            for i in range(start,n+1):
                new = lists + [i]
                if len(new) == k:
                    output.append(new)
                    continue
                huisu(new,i+1)      
        huisu([],1)
        return output

46. 全排列

给定一个没有重复数字的序列,返回其所有可能的全排列。

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

 本题的要点在于每次递归输入的序列,要刨除当前数字:lists[0:index] + lists[index+1:]

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        output = []
        length = len(nums)

        def huisu(zuhe,lists):
            for index,num in enumerate(lists):
                new = zuhe + [num]  
                if len(new) == length:
                    output.append(new)
                else:
                    huisu(new,lists[0:index] + lists[index+1:])      
        huisu([],nums)
        return output

 47. 全排列 II

给定一个可包含重复数字的序列,返回所有不重复的全排列。

输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

 本题与上一题的区别在于给出的数组里面含有重复的数字,因此首先要排序。之后给出两种方法

方法一:直接加上约束语句if new not in output,也就是不进行枝剪,速度较慢

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        output = []
        length = len(nums)

        def huisu(zuhe,lists):
            for index,num in enumerate(lists):
                new = zuhe + [num]  
                if len(new) == length and new not in output:
                    output.append(new)
                else:
                    huisu(new,lists[0:index] + lists[index+1:])      
        huisu([],nums)
        return output

 方法二:进行枝剪

if index > 0 and lists[index] == lists[index-1]:

每当进入新的构成,先考虑该构成的首字符是否和上一个一样,如果一样的话直接跳过

    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        output = []
        length = len(nums)

        def huisu(zuhe,lists):
            for index,num in enumerate(lists):
                if index > 0 and lists[index] == lists[index-1]:
                    continue
                new = zuhe + [num]  
                if len(new) == length:
                    output.append(new)
                    return
                huisu(new,lists[0:index] + lists[index+1:])      
        huisu([],nums)
        return output

 39. 组合总和 

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。 
示例 1:输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]
示例 2:输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

这道题的要点在于对原始的数组进行了排序,并且由于一个元素可以被重复的使用,所以每次递归的数组都要包含当前的元素

这里面涉及三个枝剪条件:

  1. 如果当前的数字num>res,在当前num不满足要求,并且由于candidates元素由小到大排列,则后面的数字也不会满足要求,直接return
  2. 如果当前的组合new满足要求,将其添加到output中,并且由于candidates元素由小到大排列,则后面的数字一定比该数大,也不会满足要求,直接return 
  3. 如果此时数组candidates的第一个元素大于res,也就意味下如果继续递归的话,下一次递归会加上candidates[0],必然会使得res<0,不满足要求。此时直接continue。
    例如将示例2中的target改为7,经过3次递归后new = [2,2,2],此时res = 1,而candidates 第一个数字仍为2>res,若继续递归的话,下一个new为[2,2,2,2],res就会变为-1,小于0。为了避免这次不必要的递归,直接在前一次递归,即第3次递归的时候for循环直接跳过2,new变为[2,2,3],就会满足要求
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
            output = []     
            candidates.sort()
            def digui(zuhe,candidates, res):
                for index,num in enumerate(candidates):     
                    if res - num < 0:
                        return
                    new = zuhe + [num]
                    if res - num == 0:
                        output.append(new)
                        return
                    if res - num < candidates[0]:
                        continue
                    digui(new,candidates[index:],res-num)
            digui([],candidates, target)  
            return output  

40. 组合总和 II

给定一个可能含有重复数字的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。

说明:所有数字(包括目标数)都是正整数。
            解集不能包含重复的组合。 
示例 1:输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]
示例 2:输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]

这道题的要点在于对原始的数组进行了排序,例如对于第一个例子,[1,2,5]是一个答案,如果不排序[2,1,5]也会是一个答案。

这里面涉及三个枝剪条件:

  1. 每当进入新的构成,先考虑该构成的首字符是否和上一个一样,如果一样的话直接跳过,continue
  2. 如果当前的数字num>res,说明后面的数字也不会满足要求,直接return
  3. 如果当前的组合new满足要求,将其添加到output中,则后面的数字也不会满足要求,直接return 
    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        candidates.sort()
        output = []
        
        def digui(candidates,lists,res):
            for i,num in enumerate(candidates):
                if i > 0 and candidates[i] == candidates[i-1]:
                    continue
                if res - num < 0:
                    return
                new = lists + [num]
                if res - num == 0:
                    output.append(new)
                    return
                digui(candidates[i+1:],new,res - num)
        digui(candidates,[],target)
        return output

216. 组合总和 III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

所有数字都是正整数。
解集不能包含重复的组合。 
示例 1:

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

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

相比于上题,本题更为简单。因为组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字,故不需要在排序了。枝剪条件相同。

class Solution:
    def combinationSum3(self, k: int, n: int) -> List[List[int]]:
        output = [] 
        candicate = [i for i in range(1,10)]
        
        def huisu(zuhe,candicate,res):
            for index, num in enumerate(candicate):
                if num > res:  #如果当前的数字num>res,说明后面的数字也不会满足要求,直接return
                    return 
                new = zuhe + [num]
                if res-num == 0 and len(new) == k:     #如果num = res,将目前的组合添加进去
                    output.append(new)                 #与此同时后面的数字也不会满足要求,返回
                    return
                huisu(new,candicate[index+1:],res-num)
                
        huisu([],candicate,n)
        return output

78. 子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。

示例:输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

    def subsets(self, nums: List[int]) -> List[List[int]]:
        output = []   
        output.append([])
        def digui(zuhe,nums):
            if nums == []:
                return
            for index,num in enumerate(nums):   
                new = zuhe + [num]
                if new not in output:
                    output.append(new)
                digui(new,nums[index+1:])

        digui([],nums)  
        return output 

90. 子集 II

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

说明:解集不能包含重复的子集。

示例:输入: [1,2,2]
输出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

 因为给定的数组nums可能含有重复元素,因此需要排序

    def subsets2(self, nums):
        output = []   
        nums.sort()
        output.append([])
        def digui(zuhe,candidates):
            if candidates == []:
                return
            for index,num in enumerate(candidates): 
                if index > 0 and candidates[index] == candidates[index-1]:
                    continue
                new = zuhe + [num]
                if new not in output:
                    output.append(new)
                digui(new,candidates[index+1:])

        digui([],candidates)  
        return output 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值