力扣78题:生成子集

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。
会一些的技术:数据分析、算法、SQL、大数据相关、python
欢迎加入社区:码上找工作
作者专栏每日更新:
LeetCode解锁1000题: 打怪升级之旅
python数据分析可视化:企业实战案例
python源码解读
备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

本文详细介绍了五种生成数组子集的方法,包括回溯法、迭代法和位掩码法,提供了代码示例和性能分析,旨在帮助开发者选择最适合的实现方式

题目描述

本题来自Leetcode 78
给你一个整数数组 nums,数组中的元素 互不相同。返回该数组所有可能的子集(幂集)。

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

输入格式
  • nums:一个整数数组。
输出格式
  • 返回一个列表,其中包含所有可能的子集。

示例

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

方法一:回溯法

解题步骤
  1. 定义递归函数:使用递归函数 backtrack 来生成所有可能的子集。
  2. 递归选择:从当前位置开始,递归地选择下一个元素加入子集或不加入,直到遍历完数组。
完整的规范代码
def subsets(nums):
    """
    使用回溯法生成所有可能的子集
    :param nums: List[int], 输入的整数数组
    :return: List[List[int]], 所有可能的子集
    """
    res = []
    subset = []

    def backtrack(start):
        res.append(subset.copy())
        for i in range(start, len(nums)):
            subset.append(nums[i])
            backtrack(i + 1)
            subset.pop()

    backtrack(0)
    return res

# 示例调用
print(subsets([1, 2, 3]))  # 输出: [[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
算法分析
  • 时间复杂度:(O(N * 2^N)),其中 (N) 是数组的长度。生成所有子集并复制到输出列表中每个子集需要 (O(N)) 时间。
  • 空间复杂度:(O(N)),递归过程中的栈深度最大为 (N)。

方法二:迭代法

解题步骤
  1. 迭代添加:开始时子集只包含空集,遍历每个数字,将其添加到现有的所有子集中,形成新的子集。
完整的规范代码
def subsets(nums):
    """
    使用迭代法生成所有可能的子集
    :param nums: List[int], 输入的整数数组
    :return: List[List[int]], 所有可能的子集
    """
    res = [[]]
    for num in nums:
        res += [item + [num] for item in res]
    return res

# 示例调用
print(subsets([1, 2, 3]))  # 输出: [[],[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
算法分析
  • 时间复杂度:(O(N * 2^N)),其中 (N) 是数组的长度。每个步骤中,子集的数量加倍。
  • 空间复杂度:(O(N * 2^N)),存储所有子集。

方法三:位掩码

解题步骤
  1. 位掩码表示:每个子集可以由一个长度为 (N) 的位掩码表示,其中 (N) 是数组的长度。位掩码中的每一位表示对应位置的元素是否存在于子集中。
完整的规范代码
def subsets(nums):
    """
    使用位掩码生成所有可能的子集
    :param nums: List[int], 输入的整数数组
    :return: List[List[int]], 所有可能的子集
    """
    n = len(nums)
    res = []
    for i in range(2 ** n, 2 ** (n + 1)):
        # 生成位掩码,从 2^n 到 2^(n+1) - 1
        bitmask = bin(i)[3:]
        res.append([nums[j] for j in range(n) if bitmask[j] == '1'])
    return res

# 示例调用
print(subsets([1, 2, 3]))  # 输出: [[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]
算法分析
  • 时间复杂度:(O(N * 2^N)),需要 (2^N) 次迭代,每次迭代中构造子集的时间复杂度为 (O(N))。
  • 空间复杂度:(O(N * 2^N)),存储所有子集。

方法四:二进制数序列

解题步骤
  1. 二进制增加:每一个子集可以通过对应的二进制数来表示,通过递增二进制数生成所有可能的子集。
完整的规范代码
def subsets(nums):
    """
    使用二进制数序列生成所有可能的子集
    :param nums: List[int], 输入的整数数组
    :return: List[List[int]], 所有可能的子集
    """
    res = []
    n = len(nums)
    for i in range(2 ** n):
        subset = [nums[j] for j in range(n) if (i & (1 << j))]
        res.append(subset)
    returnres

# 示例调用
print(subsets([1, 2, 3]))  # 输出: [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
算法分析
  • 时间复杂度:(O(N * 2^N)),其中 (N) 是数组的长度。需要 (2^N) 次迭代,每次迭代中构造子集的时间复杂度为 (O(N))。
  • 空间复杂度:(O(N * 2^N)),存储所有子集。

方法五:库函数

解题步骤
  1. 使用库函数:利用 Python 的 itertools 模块中的 combinations 函数来生成所有可能的子集。
完整的规范代码
from itertools import combinations

def subsets(nums):
    """
    使用库函数生成所有可能的子集
    :param nums: List[int], 输入的整数数组
    :return: List[List[int]], 所有可能的子集
    """
    res = []
    for i in range(len(nums) + 1):
        for combo in combinations(nums, i):
            res.append(list(combo))
    return res

# 示例调用
print(subsets([1, 2, 3]))  # 输出: [[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
算法分析
  • 时间复杂度:(O(N * 2^N)),尽管内置函数高效,但基本上还是需要遍历所有可能的组合。
  • 空间复杂度:(O(N * 2^N)),需要存储 (2^N) 个子集,每个子集最多含有 (N) 个元素。

不同算法的优劣势对比

特征方法一:回溯法方法二:迭代法方法三:位掩码方法四:二进制数序列方法五:库函数
时间复杂度(O(N * 2^N))(O(N * 2^N))(O(N * 2^N))(O(N * 2^N))(O(N * 2^N))
空间复杂度(O(N))(O(N * 2^N))(O(N * 2^N))(O(N * 2^N))(O(N * 2^N))
优势易于理解,灵活直接且简单利用位操作效率高直观且容易实现利用成熟的库,代码简洁
劣势递归可能复杂子集构建重复需理解位操作直接处理二进制较抽象性能依赖库实现,灵活性差

应用示例

数据科学:在特征选择中,可以用来生成特征的所有可能组合,帮助分析哪些特征组合对模型效果最好。

机器学习:在进行模型训练之前,可能需要对不同的特征组合进行测试,以确定最有效的特征集。

教学与研究:在算法课程中,可以使用这些方法来教学组合生成的不同技术和优化方法。

这些方法提供了多种生成子集的途径,适用于不同的场景和需求,选择合适的方法可以提高代码效率和可读性。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据分析螺丝钉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值