一: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; } };