题目链接:https://leetcode-cn.com/problems/subsets/
题目描述
思路
1 回溯-辅助数组记录是否取某个位置元素
复杂度分析
时间复杂度:O(2^n)
空间复杂度:O(n)
/*
* 回溯算法
* 类似0-1背包,每个位置元素有选和不选两种选择,组合数共2^n种
*
*/
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ret;
if(nums.empty()) return ret;
vector<bool > take(nums.size(), false); // 辅助数组用来标记该位置元素是否选该位置元素
subsetsCore(nums,0,take,ret);
return ret;
}
void subsetsCore(vector<int> &nums, int begin, vector<bool> &take, vector<vector<int>> &ret){
// 当位置到达末尾递归结束
if (begin >= nums.size()){
vector<int> tmp;
for (int i = 0; i < nums.size(); ++i) {
if(take[i]) // 取take数组中值为true位置对应的元素
tmp.push_back(nums[i]);
}
ret.push_back(tmp);
return;
}
take[begin] = true;
subsetsCore(nums,begin+1, take, ret); // 递归到下一元素
take[begin] = false;
subsetsCore(nums,begin+1, take, ret);
}
};
2 回溯-位运算优化
我们知道,对于给定一个集合里,所有元素的集合它们应该满足这样一个公式: 假设所有的组合数之和为sum,则有sum = C(n, 0) + C(n, 1) + …+ C(n, n); 分别对应取集合中的一个元素,两个元素…n个元素。而通过数学公式二项式定义,这个和是等于2 ** n(2的n次方)。就是说,我们所有取的组合数为一个指数函数。
假设输入是1、2、3。
首先全部的子集为【000】【001】【010】【100】【011】【101】【110】【111】 1表示这一位的数字存在,例如 【010】 表示只含有 2
由此发现子集所代表的二进制数全部小于 1 << 数组.length 第一层循环 for (int i = 0; i < (1 << size); i++)
然后根据【i】 的二进制数中 【1】 的位置取得子集
/*
* 回溯算法优化
* 集合的每个元素,都有可以选或不选,用二进制和位运算,可以很好的表示。
* 时间复杂度O(2^N) 空间复杂度O(n)
*/
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ret;
if(nums.empty()) return ret;
for (int i = 0; i < 1<<nums.size() ; ++i) {
vector<int> tmp;
for (int j = 0; j < nums.size(); ++j) {
if((i >> j) & 1 == 1)
tmp.push_back(nums[j]);
}
ret.push_back(tmp);
}
return ret;
}
};
将内层循环的终止条件换为 j < int(log2(i))+1
进一步缩短时间
3 迭代
逐个枚举,空集的幂集只有空集,每增加一个元素,让之前幂集中的每个集合,追加这个元素,就是新增的子集。
(1)初始化ret为空集
(2)遍历nums数组:
遍历已有的ret数组:
保留原ret数组内容,并以原re所有列表后加上num作为新列表添加到ret数组
/*
* 迭代
* 时间复杂度O(2^N) 空间复杂度O(1)
*/
class SolutionI {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ret;
if(nums.empty()) return ret;
ret.push_back(vector<int> {});
for (auto num:nums){
int len = ret.size();
for (int i = 0; i < len; ++i) { // 注意需要取当前的长度不能用ret.size(),否则会陷入死循环
vector<int> tmp = ret[i];
tmp.push_back(num);
ret.push_back(tmp);
}
}
return ret;
}
};