Leetcode | Subsets I & II
Subsets 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],
[]
]
Method I
我的做法是产生只有1个数的subset,然后产生有两个数的subset。这里有这么一层关系:
n+1个数的subset=前n个数的subset + 前n个数的subset都加上第n个数
比如 S=[1,2,3],产生的序列如下:
[] ------------[]
[1]-----------[] [1]
[2] [1,2]------------[] [1] [2] [1,2]
[3] [1,3] [2,3] [1,2,3]------------[] [1] [2] [1,2] [3] [1,3] [2,3] [1,2,3]
为了保证所有集合是升序的,一开始需要对S进行排序。
1 class Solution { 2 public: 3 vector<vector<int> > subsets(vector<int> &S) { 4 vector<vector<int> > ret; 5 vector<int> v; 6 ret.push_back(v); 7 sort(S.begin(), S.end()); 8 for(int i = 0; i < S.size(); ++i) { 9 int n = ret.size(); 10 for (int j = 0; j < n; ++j) { 11 vector<int> nv(ret[j]); 12 nv.push_back(S[i]); 13 ret.push_back(nv); 14 } 15 } 16 17 return ret; 18 } 19 };
Method II
递归。每碰到一个数,要么加进去,要么不加进去。最终产生所有集合。
1 class Solution { 2 public: 3 vector<vector<int> > subsets(vector<int> &S) { 4 sort(S.begin(), S.end()); 5 vector<int> v; 6 recursive(S, 0, v); 7 return ret; 8 } 9 10 void recursive(vector<int> &S, int i, vector<int> &v) { 11 if (i == S.size()) { 12 ret.push_back(v); 13 return; 14 } 15 recursive(S, i + 1, v); 16 17 v.push_back(S[i]); 18 recursive(S, i + 1, v); 19 v.pop_back(); 20 } 21 22 private: 23 vector<vector<int> > ret; 24 };
Method III
递归的第二种实现:
1 class Solution { 2 public: 3 vector<vector<int> > subsets(vector<int> &S) { 4 sort(S.begin(), S.end()); 5 vector<int> v; 6 ret.push_back(v); 7 recursive(S, 0, v); 8 return ret; 9 } 10 11 void recursive(vector<int> &S, int i, vector<int> &v) { 12 if (i == S.size()) { 13 return; 14 } 15 16 for (int j = i; j < S.size(); ++j) { 17 v.push_back(S[j]); 18 ret.push_back(v); 19 recursive(S, j + 1, v); 20 v.pop_back(); 21 } 22 } 23 24 private: 25 vector<vector<int> > ret; 26 };
Method IV
网上有人用二进制运算。因为有n=S.size()个数,那么总共的子集数也就是1<<n,每个数的第k位相当于标志了第k个数是不是在该subset中。
class Solution { public: vector<vector<int> > subsets(vector<int> &S) { sort(S.begin(), S.end()); vector<vector<int> > ret; for (unsigned int i = 0; i < (1 << S.size()); ++i) { vector<int> v; for (int j = 0; j < S.size(); ++j) { if (i >> j & 0x01) v.push_back(S[j]); } ret.push_back(v); } return ret; } };
Subsets 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],
[]
]
Method I
如果允许重复的话,如果仍用Method I,结果集会出现重复。比如S=[1,2,2,2],产生过程如下:
[]
[1]
[2][1,2]
[2][1,2][2,2][1,2,2]
[2][1,2][2,2][1,2,2][2,2,2][1,2,2,2]
红色部分就重复了。观察到有这么一些规律:
1. 如果S[i] != S[i - 1],那么和原来一样,结果集每一个subset都可以生成一个新的子集;
2. 如果S[i] == S[i - 1],那么在处理S[i - 1]时生成了多少个子集,只用这些子集来生成新的子集。
比如S[1] != S[0],所以结果集里面{[],[1]}都用来生成新子集{[2],[1,2]};
S[2] == S[1],所以只用S[1]时生成的子集{[2],[1,2]}来生成新子集{[2,2],[1,2,2]};
S[3]==S[2],所以只用S[2]时生成的子集{[2,2],[1,2,2]}来生成新子集{[2,2,2],[1,2,2,2]};这样就保证了不会重复;
我用了一个count变量来记录需要用来生成新子集的旧子集个数;if S[i] != S[i - 1],那么count = ret.size(); 否则,count不变;
1 class Solution { 2 public: 3 vector<vector<int> > subsetsWithDup(vector<int> &S) { 4 vector<vector<int> > ret; 5 vector<int> v; 6 ret.push_back(v); 7 sort(S.begin(), S.end()); 8 int count = 1; 9 for(int i = 0; i < S.size(); ++i) { 10 int n = ret.size(); 11 if (i > 0 && S[i - 1] != S[i]) { 12 count = n; 13 } 14 for (int j = n - count; j < n; ++j) { 15 vector<int> nv(ret[j]); 16 nv.push_back(S[i]); 17 ret.push_back(nv); 18 } 19 } 20 21 return ret; 22 } 23 };
Method II
仍用subset I 的MethodII的递归实现方式。主要是在去重。
每碰到一个数,要么加进去,要么不加进去。最终产生所有集合。
不加进去的逻辑是,如果检测到出现重复的数,就把后面的重复数都略过。这样对应于前面把2这个数加进去了,后面重复出现的2就略过了。
加进去的逻辑是,无论重复与否,都要加进去,然后进去下一个数。
class Solution { public: vector<vector<int> > subsetsWithDup(vector<int> &S) { sort(S.begin(), S.end()); vector<int> v; recursive(S, 0, v); return ret; } void recursive(vector<int> &S, int i, vector<int> &v) { if (i >= S.size()) { ret.push_back(v); return; } int j = i + 1; while (j < S.size() && S[j] == S[j - 1]) j++; recursive(S, j, v); v.push_back(S[i]); recursive(S, i + 1, v); v.pop_back(); } private: vector<vector<int> > ret; };
MethodIII和MethodIV的修改网上都有。不过个人觉得递归的实现理解起来比较难。还是自己想出来的Method I好哈。。。。