子集和问题 算法_LeetCode 题解 | 78.子集

7aabd0a6c7370da64a618ef49850b97e.png

力扣 78. 子集 (点击查看题目)

力扣​leetcode-cn.com

题目描述

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

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

示例:

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

解决方案

观察全排列 / 组合 / 子集问题,它们比较相似,且可以使用一些通用策略解决。

首先,它们的解空间非常大:

  • 全排列:
  • 组合:
  • 子集:
    ,每个元素都可能存在或不存在。

在它们的指数级解法中,要确保生成的结果 完整无冗余,有三种常用的方法:

  • 递归
  • 回溯
  • 基于二进制位掩码和对应位掩码之间的映射字典生成排列 / 组合 / 子集

相比前两种方法,第三种方法将每种情况都简化为二进制数,易于实现和验证。

此外,第三种方法具有最优的时间复杂度,可以生成按照字典顺序的输出结果。

方法一:递归

思路

开始假设输出子集为空,每一步都向子集添加新的整数,并生成新的子集。

003d04d1e2e1a98d3caf6a68006a270b.png

Python 实现

class 

Java 实现

class 

复杂度分析

时间复杂度:

,生成所有子集,并复制到输出结果中。

空间复杂度:

,这是子集的数量。

对于给定的任意元素,它在子集中有两种情况,存在或者不存在(对应二进制中的 0 和 1)。因此,

个数字共有
个子集。

方法二:回溯

算法

幂集是所有长度从 0 到 n 所有子集的组合。

根据定义,该问题可以看作是从序列中生成幂集。

遍历 子集长度,通过 回溯 生成所有给定长度的子集。

e761ab23f06400f338b2ecd2ee5964a7.png

回溯法是一种探索所有潜在可能性找到解决方案的算法。如果当前方案不是正确的解决方案,或者不是最后一个正确的解决方案,则回溯法通过修改上一步的值继续寻找解决方案。

deefb8e70ea2f7f3c2f129f6bfdaefb0.png

算法

定义一个回溯方法 backtrack(first, curr),第一个参数为索引 first,第二个参数为当前子集 curr。

  • 如果当前子集构造完成,将它添加到输出集合中。
  • 否则,从 first 到 n 遍历索引 i。
  • 将整数 nums[i] 添加到当前子集 curr。
  • 继续向子集中添加整数:backtrack(i + 1, curr)。
  • 从 curr 中删除 nums[i] 进行回溯。

Python 实现

class 

Java 实现

class 

复杂度分析

时间复杂度:

,生成所有子集,并复制到输出集合中。

空间复杂度:

,存储所有子集,共
个元素,每个元素都有可能存在或者不存在。

方法三:字典排序(二进制排序) 子集

思路

该方法思路来自于 Donald E. Knuth。

将每个子集映射到长度为 n 的位掩码中,其中第 i 位掩码 nums[i] 为 1,表示第 i 个元素在子集中;如果第 i 位掩码 nums[i] 为 0,表示第 i 个元素不在子集中。

c594626cb716db75ddd61bdc38505639.png

例如,位掩码 0..00(全 0)表示空子集,位掩码 1..11(全 1)表示输入数组 nums。

因此要生成所有子集,只需要生成从 0..00 到 1..11 的所有 n 位掩码。

乍看起来生成二进制数很简单,但如何处理左边填充 0 是一个问题。因为必须生成固定长度的位掩码:例如 001,而不是 1。因此可以使用一些位操作技巧:

Python 实现

nth_bit 

Java 实现

int 

或者使用简单但低效的迭代进行控制:

Python 实现

for 

Java 实现

for 

算法

  • 生成所有长度为 n 的二进制位掩码。
  • 将每个子集都映射到一个位掩码数:位掩码中第 i 位如果是 1 表示子集中存在 nums[i],0 表示子集中不存在 nums[i]。
  • 返回子集列表。

Python 实现

class 

Java 实现

class 

复杂度分析

时间复杂度:

,生成所有的子集,并复制到输出列表中。

空间复杂度:

,存储所有子集,共
个元素,每个元素都有可能存在或者不存在。

本文作者:力扣

声明:本文归“力扣”版权所有,如需转载请联系。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值