如何求解数学集合问题中的子集呢?
题目描述
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
解题思路
我们可以使用经典的回溯法解题模板,来求解这个问题:
相关代码如下:
class Solution:
def subsets(self, nums):
result = []
def recursive(select, allnums):
if select not in result:
result.append(select.copy())
for i, num in enumerate(allnums):
select.append(num)
recursive(select, allnums[i + 1:])
select.remove(num)
recursive([], nums)
return result
这种经典的回溯解法当然是万能的,但是时间复杂度稍微有点高。我们在这里来介绍一种新的方法。
迭代法实现子集枚举
记原序列中元素的总数为 n
。原序列中的每个数字 a_i
的状态可能有两种,即「在子集中」和「不在子集中」。我们用 1
表示「在子集中」,0
表示不在子集中,那么每一个子集可以对应一个长度为 n
的 0/1序列,第 i
位表示 a_i
是否在子集中。例如,n = 3 ,a = { 5, 2, 9 }
时:
0/1 序列 | 子集 | 0/10/1 序列对应的二进制数 |
---|---|---|
000 | { } | 0 |
001 | { 9 } | 1 |
010 | { 2 } | 2 |
011 | { 2, 9 } | 3 |
100 | { 5 } | 4 |
101 | { 5, 9 } | 5 |
110 | { 5, 2 } | 6 |
111 | { 5, 2, 9 } | 7 |
可以发现 0/1序列对应的二进制数正好从 0 到 2^n - 1。我们可以枚举 mask∈[0,2^n−1],mask 的二进制表示是一个 0/1序列,我们可以按照这个 0/1序列在原集合当中取数。当我们枚举完所有 2^n个 mask,我们也就能构造出所有的子集。
代码实现
class Solution:
def subsets(self, nums):
result = []
n = len(nums)
for mask in range(1<<n):
select = []
for i in range(n):
if mask & 1<<i != 0:
select.append(nums[i])
result.append(select)
return result
class Solution {
List<Integer> t = new ArrayList<Integer>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> subsets(int[] nums) {
int n = nums.length;
for (int mask = 0; mask < (1 << n); ++mask) {
t.clear();
for (int i = 0; i < n; ++i) {
if ((mask & (1 << i)) != 0) {
t.add(nums[i]);
}
}
ans.add(new ArrayList<Integer>(t));
}
return ans;
}
}
参考文献
https://leetcode-cn.com/problems/subsets/solution/zi-ji-by-leetcode-solution/
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!