【算法数据结构】回溯模板 求数组的子集

 

在 Python 中,求一个数组的所有子集可以通过多种方法实现。以下是几种高效且简洁的实现方式,包括‌迭代法‌、‌回溯法‌和‌位运算法‌,并附有详细解释和代码示例。


方法一:迭代法(推荐)

核心思想‌:
逐步构建子集,每次将当前元素添加到所有现有子集中,生成新的子集并合并到结果中。

Python 实现‌:

 
  

pythonCopy Code

示例和验证‌:

def subsets(nums):

    res = [[]] 
    for num in nums:
    res += [subset + [num] for subset in res] 
    return res

pythonCopy Code

print(subsets([1, 2, 3])) # 输出:[[], , , [1,2], , [1,3], [2,3], [1,2,3]]

复杂度分析‌:

  • 时间复杂度‌:‌O(n×2ⁿ)‌,每个元素处理时会生成两倍于当前数量的子集。
  • 空间复杂度‌:‌O(n×2ⁿ)‌,存储所有子集。

方法二:回溯法

核心思想‌:
通过递归枚举所有可能的子集,利用回溯撤销选择的状态,避免重复计算。

Python 实现‌:

pythonCopy Code

示例和验证‌:

pythonCopy Code

print(subsets_backtrack([1, 2, 3])) # 输出:[[], , [1,2], [1,2,3], [1,3], , [2,3], ]

复杂度分析‌:

  • 时间复杂度‌:‌O(n×2ⁿ)‌,每个元素有两个选择(选或不选)。
  • 空间复杂度‌:‌O(n)‌,递归栈深度为数组长度。

方法三:位运算法

核心思想‌:
每个子集对应一个二进制掩码,掩码的每一位表示是否选中对应位置的元素。

Python 实现‌:

pythonCopy Code

def subsets_bit(nums):
     n = len(nums)
     res = [] 
     for mask in range(1 << n):
        subset = [nums[i] 
     for i in range(n) :
        if mask & (1 << i)] 
            res.append(subset) 
            return res

示例和验证‌:

 
  

pythonCopy Code

print(subsets_bit([1, 2, 3])) # 输出:[[], , , [1,2], , [1,3], [2,3], [1,2,3]]

复杂度分析‌:

  • 时间复杂度‌:‌O(n×2ⁿ)‌,遍历所有可能的二进制掩码。
  • 空间复杂度‌:‌O(n×2ⁿ)‌,存储所有子集。

方法对比与选择

方法 优点 缺点
迭代法 代码简洁,无需递归 内存占用较高(需存储所有子集)
回溯法 显式状态管理,适合复杂剪枝场景 递归可能栈溢出
位运算法 内存效率高 不适用于大数组(n > 20)

推荐选择‌:

  • 优先迭代法‌:代码最简洁,适合大多数场景。
  • 需要剪枝时选回溯法‌:例如求满足条件的子集。
  • 小数组时选位运算法‌:内存效率高。

验证子集数量

所有子集的数量应为 ‌2ⁿ‌(n 为数组长度),可用以下公式验证结果正确性:

 
  

pythonCopy Code

nums = [1, 2, 3] 
print(len(subsets(nums)) == 2 ** len(nums)) 
# 输出 True

题目: 

1863. 找出所有子集的异或总和再求和 - 力扣(LeetCode)

class Solution:

    def subsetXORSum(self, nums: List[int]) -> int:
        res = []
        self.backtrack(res, nums, 0, [])
        # print(self.res)
        count = 0
        count_list = []
        for it in res:
            if len(it) > 0:
                num = 0
                for n in it:
                    num = num ^ n
                count_list.append(num)
        # print(count_list)
        # print(res)
        for i in count_list:
            count += i
        return count

        return 0

    def backtrack(self, res, nums, start, path):
        res.append(path.copy())  # 拷贝
        for i in range(start, len(nums)):
            path.append(nums[i])
            self.backtrack(res, nums, i + 1, path)
            path.pop()

你可能感兴趣的:(算法,数据结构)