LeetCode每日一题(集合问题)

78.子集

题目概述

子集问题,看到这种题,肯定是可以用DFS加递归可以做的。首先分析思路,这种类似于全排列的情况,并且顺序不同算一种的话,其实就只要递归遍历就可以了

第一种方法,回溯递归

这题其实就是类似于全排列的问题,显而易见的是顺利不同的组合算是一个,例如[1,2,3], [1,3,2] , 因为数组中的元素是排序且无重复的,因此只需要考虑在递归进入下一阶段的时候对输入的begin+1 就可以了

res= [[]]
for i in range(begin,k):
    temp.append(nums[i])
    res.append(temp.copy())
    dfs(i+1,temp)
    temp.pop()

这里的递归最重要的方法是 递归向前,若begin 进入的值为0,则下一步就是dfs(1,temp), 由于进入的时候加上了nums[i] 所以回溯的时候必须把之前的最后一个元素给pop() 掉

思路二

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        for i in range(len(nums)+1):
            for tmp in itertools.combinations(nums, i):
                res.append(tmp)
        return res

思路三

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = [[]]
        for i in nums:
            res = res + [[i] + num for num in res]
        return res

相同的还有以下多种类似的问题

这里先把相同的问题,子集II写出来
初始的版本,未使用剪枝,只是回溯递归

   # 子集II
        # 思路同样可以使用回溯递归方法
        # 与子集I 不同的地方是此时数组内会有相同元素
        k = len(nums)
        res=[[]]
        nums.sort()
        def dfs(begin,temp,nums):
            for i in range(begin,k):  
                temp.append(nums[i])
                if temp not in res:
                    res.append(temp.copy())
                dfs(i+1,temp,nums)
                temp.pop()
        dfs(0,[],nums)
        return res

显然这样的方式速度并不快,于是就要思考如何在出现重复的情况下提前剪枝。

        nums.sort()
        self.res = []
        self.backtrack([], 0, nums, 0)
        return self.res

    def backtrack(self, sol, index, nums, pre_used):
        if index == len(nums):
            self.res.append(sol)
            return
        if not (index > 0 and nums[index] == nums[index-1] and pre_used == 0):
            pre_used = 1
            self.backtrack(sol+[nums[index]], index+1, nums, pre_used)
            pre_used = 0
        self.backtrack(sol, index+1, nums, 0)

这里的剪枝去掉了对元素的去重判断,转而在出现重复之前做剪枝处理,节省空间开支

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值