回溯算法-子集

一个集合的所有子集从空集开始,一直到自己本身,都是其子集
leetcode 78子集
没有重复元素集合的子集
在这里插入图片描述
同样的,套回溯模板

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        if not nums:
            return [[]]
        n = len(nums)
        res = []
        def backtrace(start, tmp):
        	#所有情况都是满足要求的,所以直接保存结果
            res.append(tmp[:])
            for i in range(start, n):
            	#这里没有使用状态数组的原因是,我们每次回溯遍历的起点都不一样
            	#也就是说,根本就不会发生再次选择前面元素的情况
            	#做选择
                tmp.append(nums[i])
                backtrace(i+1, tmp)
                #撤销选择
                tmp.pop()
        backtrace(0, [])
        return res

子集 II
那么有重复元素的集合子集呢?
同样的,对于有重复元素的,先对数组进行排序,这根处理全排列是一样的;第二个相同的,也需要一个状态数组来记录元素使用情况,主要是激励相同元素的使用情况,避免出现重复的子集。
在这里插入图片描述

class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        if not nums:
            return [[]]
        nums.sort()
        n = len(nums)
        res = []
        #count = 0
        status = [False] * n
        def backtrace(start, tmp):
            res.append(tmp[:])
            #这部分代码和有重复元素的全排列代码基本一样,唯一不同的是,每次的遍历起点不一样
            for i in range(start, n):
                if not status[i]:
                    if i > 0 and nums[i] == nums[i-1] and not status[i-1]:
                        continue
                    status[i] = True
                    tmp.append(nums[i])
                    backtrace(i+1, tmp)
                    tmp.pop()
                    status[i] = False
        backtrace(0, [])
        return res

这两道题,包括全排列的那两道,整体代码是不是很像,就是套回溯模板,如出一辙。

leetcode 1079 活字印刷
综合了全排列和子集的一道题
在这里插入图片描述
思路:先求出原字符串的所有子集,因为有重复的元素,处理方式如上;然后再求出每一个子集的所有全排列,求和即为最终结果。就是先求子集,再求全排列。这两部分代码,基本上不需要改动,增加一个计数的变量即可

class Solution:
    def numTilePossibilities(self, tiles: str) -> int:
        if not tiles:
            return 0
        tiles = list(tiles)
        tiles.sort()
        n = len(tiles)
        res1 = []
        status1 = [False] * n
        #求有重复元素集合的子集,完全不用改动
        def backtrace1(start, tmp):
            res1.append(tmp[:])
            for i in range(start, n):
                if not status1[i]:
                    if i > 0 and tiles[i] == tiles[i-1] and not status1[i-1]:
                        continue
                status1[i] = True
                tmp.append(tiles[i])
                backtrace1(i+1, tmp)
                tmp.pop()
                status1[i] = False
		#求全排列
        count = 0
        def backtrace2(res, tmp, length):
            nonlocal count
            if len(tmp) == length:
                count += 1
                #res2.append(tmp[:])
                return
            for i in range(length):
                if not status2[i]:
                    if i > 0 and res[i] == res[i-1] and not status2[i-1]:
                        continue
                    status2[i] = True
                    tmp.append(res[i])
                    backtrace2(res, tmp, length)
                    tmp.pop()
                    status2[i] = False
        backtrace1(0, [])
        #第一个子集是空集,所以从1开始
        for i in range(1, len(res1)):
            length = len(res1[i])
            status2 = [False] * length
            backtrace2(res1[i], [], length)
        return count

类似的求子集的题目还有
39.组合总和
40.组合总和II
同样是分有无重复元素,有重复元素的就用状态数组记录一下,没有就不用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值