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好哈。。。。

posted @ 2014-05-07 00:26  linyx  阅读(216)  评论(0编辑  收藏  举报