Leetcode OJ: Subsets I/II
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], [] ]
罗列所有的子集,要求每一个子集的元素有序,且无重复子集。而且保证每个元素都是无重复(distinct)的。
很自然,第一想法就是递归,回溯那一套,写完后提交,可惜没通过。。
原因不明,N天过后搜了下才知道需要有序,所以还需要先sort。
第一次AC代码如下:
1 class Solution { 2 public: 3 void subsetsImpl(vector<int>& S, int index, vector<int>& item, vector<vector<int> >& ret) { 4 if (index == S.size()) { 5 ret.push_back(item); 6 return; 7 } 8 subsetsImpl(S, index + 1, item, ret); 9 item.push_back(S[index]); 10 subsetsImpl(S, index + 1, item, ret); 11 item.pop_back(); 12 } 13 vector<vector<int> > subsets(vector<int> &S) { 14 vector<vector<int> > ret; 15 vector<int> item; 16 sort(S.begin(), S.end()); 17 subsetsImpl(S, 0, item, ret); 18 return ret; 19 } 20 };
时间复杂度O(n*2^n),n为S的长度,2^n是组合个数,无可避免。
在网上还搜到了另外的一些非递归实现的解法
有利用二进制数的,个人觉得这方法约束太大了,因为二进制数超过64位了就得自己去实现一个了,比较麻烦,而且复杂度没有比以上方法提升太多。
还有一种动态规划的方法,还是比较通用的,个人比较推荐,复杂度O(2^n)。
思路就是subset(n) = join(subset(n-1), (subset(n-1), S[n]))
每考虑一个新的元素,就是把这个元素与之前得到的结果都结合一次,并进之前的结果,得出新的结果。
代码如下:
1 class Solution { 2 public: 3 vector<vector<int> > subsets(vector<int> &S) { 4 sort(S.begin(), S.end()); 5 vector<vector<int> > ret(1); 6 for (auto it = S.begin(); it != S.end(); ++it) { 7 int j = ret.size(); 8 while (j--) { 9 ret.push_back(ret[j]); 10 ret.back().push_back(*it); 11 } 12 } 13 return ret; 14 } 15 };
如果元素中有重复怎么办呢?
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], [] ]
按照上面提到的动态规划的思想,我们看看有重复的过程会是怎样的。
S=[1, 2, 2]
[] // 空
[1] // 1与之前的结合
[2]
[1][2] // 2 与之前的所有结合
[2]
[1][2]
[2][2]
[1][2][2] // 2与之前的结合删掉了2个
1 class Solution { 2 public: 3 vector<vector<int> > subsetsWithDup(vector<int> &S) { 4 sort(S.begin(), S.end()); 5 vector<vector<int> > ret(1); 6 int pre = 0; 7 for (auto it = S.begin(); it != S.end(); ++it) { 8 int upper = ret.size(); 9 int bottom = (it != S.begin() && *it == *(it-1)) ? pre : 0; 10 pre = upper; 11 for (int i = upper - 1; i >= bottom; --i) { 12 ret.push_back(ret[i]); 13 ret.back().push_back(*it); 14 } 15 } 16 return ret; 17 } 18 };
结构是一样的,只是加了个upper与bottom的限制。复杂度没变化。