题目如下:
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],
[]
]
分析如下:
这道题目非常有意思,它让人找出给定的集合(其中没有重复元素)的所有的子集合。
第一个思考方向,迭代。这里面又需要把一件事情想清楚,结果是要求用non-desceding顺序的,迭代的时候如何实现这一点呢?你可以每生成一个子集合就把这个子集合当做数组排序一下。但是每次都排序,比较花时间。所以可以考虑别的办法。如果对元集合进行排序,迭代的时候按照原集合的排序后的顺序逐步生成子集合呢。这样就快多了,代码量也少了。我一开始写了一个很2的代码,提交过后发现就在leetcode官网上,就有一个代码量减少50%,时间减少90%的好代码了。。。
具体的思路是这样的:
初始状态: []
第0次,加入S[0]: [], [1]
第1次,加入S[1]: [], [1], [2], [1, 2]
第1次,加入S[2]: [], [1], [2], [1, 2], [3], [1,3], [2,3], [1,2,3]
从上面可以看出,第0次->第1次,把S[1]加入到S[0]的每个subset中,形成新的subset(即[2], [1, 2])这堆新的subset和之前S[0]时候的那堆老的subset(即[], [1])一起构成S[1]的结果(即 [], [1], [2], [1, 2])。
重复这个过程直到把S的每个元素都加入了当前的集合。
第二个思考方向,递归。思路比较简单好想。
第三个思考方向,从数学角度来看,假设原集合有n个元素,那么原集合的子集合的个数是2的n次方,记为2 ^ n。对应着从0~2 ^ n - 1这2 ^ n个数。这2 ^ n个数如果用二进制表示,可以发现一共有n位。每位要么取0,要么取1。如果第i位取0,则说明元集合的第i个元素不出现在当前新生成的子集合中,反之,如果第i位取1,则说明元集合的第i个元素出现在当前新生成的子集合中。这个思路可以通过比特位操作来实现。
代码如下:
//迭代版一 40ms过大集合,目前最简洁最快速的迭代版,leetcode官网答案。先把S排序,然后用j保留vector变化前的大小,再对vector进行插入操作。 class Solution { public: vector<vector<int> > subsets(vector<int> &S) { int length=(int)S.size(); vector<vector<int>> res(1); if(length==0) return res; std::sort(S.begin(), S.end()); for(int i=0;i<S.size();++i){ int j=(int)res.size(); while(--j>=0){ //如果写为while(j-->0)也正确,不过更推荐的写法是前者。因为前自增生成左值,不需要额外的开销。详情见小结。 res.push_back(res[j]); res.back().push_back(S[i]); } } return res; } };
//迭代版二 628ms过大集合,我最开始写的很弱的版本。。。。 class Solution { public: vector<vector<int> > subsets(vector<int> &S) { vector<vector<int> > res_vec_vec; set<set<int> > res_set_set; set<set<int> >::iterator res_set_it; int length=(int)S.size(); if(length==0) return res_vec_vec; set<int> small_set; res_set_set.insert(small_set); //inset"[]" as a start. for(int i=0;i<length;i++){ set<set<int> > tmp_res_set=res_set_set; for(res_set_it=res_set_set.begin();res_set_it!=res_set_set.end();res_set_it++){ for(int k=0;k<(int)length;k++){ //bug1 一开始写为了for(int k=0;i<(int)length;k++){ set<int> tmp_small_set=*res_set_it; tmp_small_set.insert(S[k]); tmp_res_set.insert(tmp_small_set); } } res_set_set.swap(tmp_res_set); } for(res_set_it=res_set_set.begin();res_set_it!=res_set_set.end();res_set_it++){ set<int> tmp_small_set=*res_set_it; vector<int> tmp_vec; for(set<int>::iterator it=tmp_small_set.begin();it!=tmp_small_set.end();it++){ tmp_vec.push_back(*it); } res_vec_vec.push_back(tmp_vec); } return res_vec_vec; } };
//递归版一 52ms过大集合 我写的递归版 class Solution { public: set<set<int> > subsets_(vector<int> &S, int i){ if(i==-1){ set<int> set_tmp; set<set<int> > set_set_tmp; set_set_tmp.insert(set_tmp); return set_set_tmp; } set<set<int> > set_set_tmp = subsets_(S,i-1); // set<set<int> > set_set_out = subsets_(S,i-1); set<set<int> >::iterator set_set_it=set_set_tmp.begin(); for(set_set_it=set_set_tmp.begin();set_set_it!=set_set_tmp.end();set_set_it++){ set<int> set_tmp=*set_set_it; set_tmp.insert(S[i]); set_set_out.insert(set_tmp); } return set_set_out; } vector<vector<int> > subsets(vector<int> &S) { int length=(int)S.size(); vector<vector<int>> res; set<set<int>> tmp; if(length==0) return res; length--; tmp=subsets_(S,length); for(set<set<int> >::iterator it=tmp.begin();it!=tmp.end();it++){ set<int> small_set=*it; vector<int> small_vec; // std::cout<<std::endl; for(set<int>::iterator small_it=small_set.begin();small_it!=small_set.end();small_it++){ small_vec.push_back(*small_it); // std::cout<<"*small_it="<<*small_it<<"\t"; } res.push_back(small_vec); } return res; } };
//模拟2的n次方的版本 leetcode官网答案 40ms过大集合 class Solution { public: vector<vector<int> > subsets(vector<int> &S) { vector< vector<int> > result; sort(S.begin(), S.end()); // Loop from 0 to 2^n - 1 for (int x = 0; x < (1 << S.size()); ++x) { vector<int> sln; for (int i = 0; i < S.size(); ++i) // If the i-th least significant bit is 1, then choose the i-th integer if (x & (1 << i)) sln.push_back(S[i]); result.push_back(sln); } return result; } };
小结:
(1) 关于前自增和后自增。在前面的迭代版一种,看到了前自增和后自增操作符。二者的区别是?
int i = 0, j; j = ++i; // j = 1 , i = 1:prefix yields incremented value j = i++; // j = 1 , i = 2:postfix yields unincremented value左值:可以出现在赋值操作左边的值。非const左值可读可写。
(2) 关于比特操作
模拟2的N次方的版本中,使用到了比特操作。
if (x & (1 << i))
这句话的意思是是什么呢?首先理解i和x。i代表了输入集合S的第i位,x代表了取值范围在0~2 ^ n -1的数字。如果x的第i位为0,那么输入集合中的元素S[i]就出现再当前子集合中,否则不出现。这句话就是去判断S[i]的第i位是不是为1的。另外这句话如果像下面这么写也是可以等价的
if (( x >> i ) & 1)
参考资料:
(1) http://discuss.leetcode.com/questions/253/subsets
(2) http://www.cnblogs.com/maxwellp/archive/2012/02/11/2346844.html
update: 2014- 12-17
思路类似上面的迭代版1,速度非常快。16ms
class Solution { public: vector<vector<int> > subsets(vector<int> &S) { std::sort(S.begin(), S.end()); vector<vector<int> > final(1); for (int i = 0; i < S.size(); ++i) { int length = final.size(); for (int j = 0; j < length; ++j) { //std::cout<<"j="<<j<<std::endl; vector<int> temp = final[j]; temp.push_back(S[i]); final.push_back(temp); } } return final; } };
update: 2014-12-17
思路类似上面的bit 操作版,16ms。 我还不太熟悉用 x & (1<<s.size())这种写法来表达,所以下面写出来的代码看起来比较丑比较原始。但是思路和比特操作的思路一样。
//版本二: bit operation class Solution { public: vector<vector<int> > subsets(vector<int> &S) { std::sort(S.begin(), S.end()); vector<vector<int> > final; vector<int> every; int total = pow(2, S.size()); int j = 0; for (int i=0; i < total; ++i) { int current = i; while(current>0) { if (current & 1) { every.push_back(S[j]); } current = current>>1; j++; } final.push_back(every); every.clear(); j = 0; } return final; } };
update: 2015-03-24
迭代版第一版本, 用result和tmp两个vector倒腾
//10ms class Solution { public: vector<vector<int> > subsets(vector<int> &S) { sort(S.begin(), S.end()); vector<vector<int> > result(1); vector<vector<int> > tmp; for (int i = 0; i < S.size(); ++i) { int length = result.size(); tmp.assign(result.begin(), result.end()); for (int j = 0; j < length; ++j) { tmp.push_back(result[j]); tmp.back().push_back(S[i]); } result.swap(tmp); } return result; } };
迭代版第二版本,其实只用result这1个vector就够了。
//9ms class Solution { public: vector<vector<int> > subsets(vector<int> &S) { sort(S.begin(), S.end()); vector<vector<int> > result(1); for (int i = 0; i < S.size(); ++i) { int length = result.size(); for (int j = 0; j < length; ++j) { result.push_back(result[j]); result.back().push_back(S[i]); } } return result; } };
DFS递归版
class Solution { public: // void print_vector(vector<int> v) { // for (int i=0; i < v.size(); ++i) { // std::cout<<v[i]<<"\t"; // } // std::cout<<std::endl; // } void helper(vector<int> &S, vector<int> &every, vector<vector<int> > &final, int begin, int end) { for (int i = begin; i < end; ++i) { every.push_back(S[i]); final.push_back(every); helper(S, every, final, i + 1, end); every.pop_back(); } } vector<vector<int> > subsets(vector<int> &S) { vector<vector<int> > final(1); vector<int> every; sort(S.begin(), S.end()); int start = 0; helper(S, every, final, start, (int)S.size()); return final; } };