子集

题目描述

来源于 https://leetcode-cn.com/

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

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

示例:

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

解法一:迭代

考虑数组 [1,2,3],从左到右来遍历数组,初始结果中只包含一个空集:

[]

现在有了数字 1,可以得到子集(加入空行,以区分原来的子集和新增的子集):

[]

[1]

加入数字 2 以后,得:

[]
[1]

[2]
[1,2]

数字 3 到来后,得到:

[]
[1]
[2]
[1,2]

[3]
[1,3]
[2,3]
[1,2,3]

可以看到,每新增一个数,产生的子集就是把之前的子集中都加入当前数字。

由此,可以写出如下代码:

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        result.reserve(pow(2, nums.size()));
        result.push_back(vector<int>());
        for(int n: nums){
            int len = result.size();
            for(int i=0; i<len; i++){
                vector<int> subset = result[i];
                subset.push_back(n);
                result.push_back(::move(subset));
            }
        }
        return result;
    }
};

解法二:位图

子集,实际上就是考虑每个数字是否出现在集合中。一个数出现与不出现共两种情况,因此 n 个数的子集共有 2^n 个。把这 n 个数对应到 n 位二进制上,每个数出现与否体现为二进制位为 0 或 1。因此,在 [0,2^n) 之间的每一个数的二进制信息就能唯一确定一个子集。

比如对于集合 [1,2,3], 001 就表示只取第 1 个元素,构成集合为 {1}101 就表示只取 1 和 3,表集合 {1,3}

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        unsigned int len = pow(2, nums.size());

        for(unsigned int i = 0; i < len; i++){
            vector<int> subset;
            unsigned int n = i;
            for(int j=0; n != 0; j++){
                if(n & 1){
                    subset.push_back(nums[j]);
                }
                n >>= 1;
            }
            result.push_back(::move(subset));
        }
        return result;
    }
};

当待求子集的集合长度不大于 32 的时候,可以使用 int 来记录子集的总数量,当大于 32 的时候,可以使用 64 位整数,不过就算 32 位,也已经非常大了。