一:subset I
题目:
Given a set of distinct integers, S, return all possible subsets.
Note:
- Elements in a subset must be in non-descending order.
- The solution set must not contain duplicate subsets.
For example,
If S = [1,2,3]
, a solution is:
[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]链接:https://leetcode.com/problems/subsets/
分析:说白了就是求不同元素所构成的所有子集,这里提供三种方法
(1)递归————较难理解
对结合[1,2,3] 从0结点的1开始,都有选择或者不选,不选为空,放在左子树,选择放在由子树,这样就得到一棵完全二叉树,其中叶子结点就是我们所求。理解代码可以试着从只有1个结点开始思考。
图中与代码有差异,左右刚好相反
class Solution {
public:
void recursion(vector<int> temp, vector<int> &S, int level, vector<vector<int> > &result){
if(level == S.size()){
result.push_back(temp);
return;
}
recursion(temp, S, level+1, result); // 不选元素s[level] 通过只有1个元素来理解递归
temp.push_back(S[level]);
recursion(temp, S, level+1, result); // 选择元素s[level]
}
vector<vector<int> > subsets(vector<int> &S) {
sort(S.begin(), S.end());
vector<int> temp;
vector<vector<int> > result;
recursion(temp, S, 0, result);
return result;
}
};
(2) 迭代
通过上图我们可以看出,每次都是在原有的子集后面添加新元素S[i] 并加入到result中就会得到另外一个子集,因此可以采用迭代处理。
class Solution {
public:
void iterSet(int i, vector<vector<int> > &result, vector<int> &S){
int n = result.size();
for(int j = 0; j < n; j++){
vector<int> temp = result[j]; //集合中原有的子集保持不变,
temp.push_back(i); //但是对于每个子集都新遍历的一个元素S[i]加入构成新子集
result.push_back(temp);
}
}
vector<vector<int> > subsets(vector<int> &S) {
vector<vector<int> > result;
sort(S.begin(), S.end());
vector<int> temp;
result.push_back(temp); // 集合中已有空集
for(int i = 0; i < S.size(); i++){ // 对于每个元素都遍历一遍,
iterSet(S[i], result, S);
}
return result;
}
};
(3)位操作
对于n个元素,每个元素要么加入要么不加入,则会得到一个n位的二进制串,n为二进制串可以看做是一个状态或者说一个子集,每个元素与一位一一对应,状态的大小为1<<n. 对于每个状态j,我们通过判断第k位是否为1,为1则将S[k]加入vec,判断结束,将vec加入result即可。
class Solution {
public:
vector<vector<int> > subsets(vector<int> &S) {
vector<vector<int> > result;
int n = S.size();
int max = 1 << n; // 最多状态数 存在用1 不存在用0
int i = 0;
sort(S.begin(), S.end());
while(i < max){ // 对每一种状态进行遍历 并将其所对应的元素加入到集合中
int j = i;
vector<int> temp;
/*for(int k= 0; k < n; k++){
if((j & (1<<k)) != 0) temp.push_back(S[k]);
}*/
int index = 0;
while(j>0){
if(j&1) temp.push_back(S[index]); // 第index为1 则加入s[index]位
j = j >> 1;
index++;
}
result.push_back(temp);
i++;
}
return result;
}
};
二:subset II
题目:
Given a collection of integers that might contain duplicates, S, return all possible subsets.
Note:
- Elements in a subset must be in non-descending order.
- The solution set must not contain duplicate subsets.
For example,
If S = [1,2,2]
, a solution is:
[ [2], [1], [1,2,2], [2,2], [1,2], [] ]链接:https://leetcode.com/problems/subsets-ii/
分析:与unique paths的区别在于允许元素重复,这是当元素重复时,我们会发现每次加入的子集都是上次迭代的子集,因此需要对迭代法略做改进。
class Solution {
public:
void iterSet(int i, vector<vector<int> > &result, vector<int> &S, int &t){
int n = result.size();
int count = 0;
int j = 0;
if(i-1>=0 && S[i]==S[i-1])j = n-t; // 如果相等 则只能在上次加入的子集中加入s[i]
for(;j < n; j++){
vector<int> temp = result[j]; //集合中原有的子集保持不变,
temp.push_back(S[i]); //但是对于每个子集都新遍历的一个元素S[i]加入构成新子集
result.push_back(temp);
count++; //主要用来记录每次迭代加入集合个数
}
t = count;
}
vector<vector<int> > subsetsWithDup(vector<int> &S) {
vector<vector<int> > result;
sort(S.begin(), S.end());
vector<int> temp;
result.push_back(temp); // 集合中已有空集
int t = 0;
for(int i = 0; i < S.size(); i++){ // 对于每个元素都遍历一遍,
//if(i-1 < 0 || S[i] != S[i-1]) t = 0;
iterSet(i, result, S, t);
}
return result;
}
};