我的思路比较常见,是用回溯法,效果一般,代码如下:
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
int n = nums.size();
for (int i = 1; i < n; i++) {
vector<int> subset = {};
backTrack(nums, res, subset, i, 0);
}
vector<int> subset = {};
res.push_back(subset);
res.push_back(nums);
return res;
}
void backTrack(vector<int>& nums, vector<vector<int>>& res, vector<int>& subset, int len, int index) {
if (subset.size() == len) {
res.push_back(subset);
return;
}
for (int j = index; j < nums.size(); j++) {
subset.push_back(nums.at(j));
backTrack(nums, res, subset, len, j + 1);
subset.pop_back();
}
}
};
看到一个解法比较有意思,运行时间和我的一样,12ms,主要是利用二进制特性,先上代码:
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> result;
int len = (0 ^ (1 << nums.size()));
for (int i = 0; i < len; i++) {
vector<int> t;
int k = i;
int j = 0;
while (k) {
if(k % 2)
t.push_back(nums.at(j));
k = k / 2;
j++;
}
result.push_back(t);
}
return result;
}
};
这里有两点需要注意的是:
1、len = (0 ^ (1 << nums.size())):这一步是求出有子集的个数,有元素
个,那么子集的个数为
个,证明见链接,如果输入为 [1, 2, 3],自己个数为8。
2、k % 2 和 k / 2:其实把子集这个问题转换成二进制发现,结果过就是对三位进行编码,如果子集为空集,则用三位编码得000,若子集为原数组本身,则用三位编码得111。那么求余和整除操作其实就是把三位编码中的1找出来,若为1,则 j++,然后push_back。
下面的代码是最快实现,也就是另一种形式的回溯:
class Solution {
public:
vector<vector<int>> res;
vector<bool> visit;
// p:位置 表示当前 执行到要确定 哪一个位置了
void permuten(vector<int>& nums, int p, vector<int>& tmp, int count, int begin)
{
if (p == count)
{
res.push_back(tmp);
return;
}
for(int i = begin; i < nums.size(); i++)
{
if(!visit[i])
{
// 一定加上改变 visit
visit[i] = true;
// 当前位置上的数确定了
tmp.push_back(nums[i]);
// 继续确定下一个位置
permuten(nums, p+1, tmp, count, i + 1);
// 执行到这里 表示所有位置上的数已经确定了 本次可以结束了
// 弹出当前的数 看还有没有其他可能
// 回溯
tmp.pop_back();
// 一定加上改变 visit
visit[i] = false;
}
}
}
vector<vector<int>> subsets(vector<int>& nums) {
res.clear();
if(nums.size() == 0) return {};
res.push_back({});
for(int i = 1; i < nums.size() + 1; i++)
{
vector<int> tmp;
// vector赋值
visit = vector<bool>(i ,false);
permuten(nums, 0, tmp, i, 0);
}
return res;
}
};