leetcode78_子集

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

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

示例:

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

用位技巧
记原序列中元素的总数为 n。原序列中的每个数字的状态可能有两种,即「在子集中」和「不在子集中」。我们用 1 表示「在子集中」, 0表示不在子集中,那么每一个子集可以对应一个长度为 n 的 二进制序列,第 i 位表示 对应元素是否在子集中。
如 2 3 4 n=3
序列 子集 序列对应的二进制数
0 [] 000
1 [4] 001
2 [3] 010
3 [3,4] 011
4 [2] 100
5 [2,4] 101
6 [2,3] 110
7 [2,3,4] 111
可以发现序列对应的二进制数正好从0到2^n-1 。我们可以枚举这些序列,按照当前数序列对应的二进制数在原集合当中取数。

下面注意几个位技巧 a << n 表示将a这个数对应的二进制为左移n位,在数学意义上就是乘以n个2,每移动1位就是乘以1个2。因此2^n-1就是1<<n - 1

另外还有与操作的一个技巧,就是当一个数a的二级制序列只有第 i 位是1其他都是0的时候,用a和任意一个数b做与操作,如果结果不为0说明b在第 i 位也是1,如果结果为0说明b在第 i 位上为 0。因此用a可以去检验任何一个数b的二进制的第i位是否为1。因此我们在代码里面用 1 << pos pos=【0-n)去逐个检验长度为n的二进制序列mask的第pos位是否为1,如果为1, 则把原始数组的第pos个加到结果中去

由于有2^n个序列,每个序列都要遍历n个位置去检验是否该把这个位置的元素加到结果中,则时间复杂度O(2的n次方 * n) 。 空间复杂度O(n)因为这是临时数组sub的大小

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        n = len(nums)
        output = []
        for mask in range(0, 1 << n):
            sub = []
            for pos in range(n):
                if mask & (1 << pos):
                    sub.append(nums[pos])
            output.append(sub)
        return output 

1 全局变量: 保存结果
2 参数设计: 递归函数的参数,是将上一次操作的合法状态当作下一次操作的初始位置。这里的参数,我理解为两种参数:状态变量和条件变量。(1)状态变量(state)就是最后结果(result)要保存的值;(2)条件变量就是决定搜索是否完毕或者合法的值。
3 完成条件: 完成条件是决定 状态变量和条件变量 在取什么值时可以判定整个搜索流程结束。搜索流程结束有两种含义: 搜索成功并保存结果 和 搜索失败并返回上一次状态。
4 递归过程: 传递当前状态给下一次递归进行搜索。

res = []    # 定义全局变量保存最终结果
state = []  # 定义状态变量保存当前状态
p,q,r       # 定义条件变量(一般条件变量就是题目直接给的参数)
def back(状态,条件1,条件2,……):
    if # 不满足合法条件(可以说是剪枝,可选)
        return
    if # 状态满足最终要求(可选)
        res.append(state)   # 加入结果,必须有
        return 
    # 主要递归过程,一般是带有 循环体 或者 条件体
    for # 满足执行条件
    	if  # 满足执行条件
       		back(状态,条件1,条件2,……)
back(状态,条件1,条件2,……)
return res

这种需要告知具体路径的,就不能用动态规划了,只能回溯
1, 以当前位置为源流往下摸排所有可以跳到的位置
2, 最终递归返回源流位置
3, 然后再以下面一个位置作为源流位置,重复上述操作

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        result = []
        search = []
        self._dfs(nums, 0, search, result)
        return result
    
    def _dfs(self, nums, index, search, result):
    	# 求所有子集的问题,不需要完成条件,因为任何一个状态都是一个子集
        result.append(search.copy())
        for i in range(index, len(nums)):
            # 以当前位置为源流
            search.append(nums[i]) 
            # 往下摸排所有可以跳到的位置 
            self._dfs(nums, i + 1, search, result)
            # 最终递归返回源流位置,然后再以下面一个位置作为源流位置,重复上述操作
            search.pop()

对比 leetcode 90 子集II
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:

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

输入:nums = [0]
输出:[[],[0]]

这个例子其实每个元素依然是只能用一次,只有一个不同就是有重复元素
比如[2,2,3] ,如果按78这题的做法,可能出现[2,3], [2,3],两个2并不同,但是其实这个组合重复了。因此要加一个限制

class Solution:
    def subsetsWithDup(self, nums):
        res = []    # 定义全局变量保存最终结果
        state = []  # 定义状态变量保存当前状态
        def back(state,q):
            # 求所有子集的问题,不需要完成条件,因为任何一个状态都是一个子集
            res.append(state.copy()) 
            # 主要递归过程,一般是带有 循环体 或者 条件体
            for i in range(q,len(nums)):
            	# 由于原始数组元素有重复,需要加这个条件避免重复组合
                if (i > q) and (nums[i] == nums[i-1]):
                    continue
                state = state + [nums[i]]
                back(state,i+1)
                state.pop()
        nums.sort()
        back(state,0)
        return res

最后对比一道剑指offer 38

输入一个字符串,打印出该字符串中字符的所有排列, 字符串里面有重复元素。你可以以任意顺序返回这个字符串数组,但不能出现重复排列。

前面几道题,是组合问题,也就是所谓一个组合就是用些什么元素,元素之间的顺序是无所谓的,每种组合给出一种顺序的结果就可以了。所以在摸牌的时候,都是以下一个元素为源流往后摸牌。而这道题要求的是组合,也就是每种顺序都是一个结果。主要看下摸牌的方法,是把除了当前元素以外的其他元素全部加入下一步摸牌。

def permutation(s: str):
    res = []    # 定义全局变量保存最终结果
    state = ""  # 定义状态变量保存当前状态
    def back(state, s):
        # 全排列问题需要用上全部元素,因此有完成条件
        if len(s) == 0: # 状态满足最终要求
            res.append(state) # 加入结果
            return

        for i in range(len(s)):
            # 这个地方是同理的,由于原始字符串存在重复元素,因此
            # 如果不加这个条件,会出现重复的排列
            if (i > 0) and (s[i] == s[i-1]):
                continue
            state = state + s[i]
            back(state, s[:i]+s[i+1:])
            state = state[:-1]
    s = list(s)
    s.sort()
    back(state, s)
    return res


print(permutation('aac'))
['aac', 'aca', 'caa']
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值