Combination and Permutation 1 - General Template

1. Subsets - Leetcode 78

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],
  []
]

通用模板: 时间复杂度 O(2^n),空间复杂度 O(n)

vector<vector<int> > subsets(vector<int> &S) {
  vector<vector<int> > res;
  // 下边已经涵盖了这种情况
  // if (S.empty()) {
  //    res.push_back(vector<int>());
  //    return res;  // 输入判断
  // }
  sort(S.begin(), S.end());  // could change S or not?
  vector<int> path;
  // Don't need for combination! vector<bool> visited(S.size(), false);
  subsetsCore(S, res, path, 0);
  return res;
}
void subsetsCore(vector<int> &S, vector<vector<int> > &res, vector<int> &path, int start) {
  res.push_back(path);
  for (int i = start; i < S.size(); i++) {
    path.push_back(S[i]);              // 选
    subsetsCore(S, res, path, i + 1);  // Caution: i+1
    path.pop_back();                   // 不选, 换下一个
  }
}

其他方法,二进制模拟:

vector<vector<int> > subsets(vector<int> &S) {
  // Use binary to simulate.
  // S.size() limited to 32
  //时间复杂度 O(2^n),空间复杂度 O(1)
  //这种方法最巧妙。因为它不仅能生成子集,还能方便的表示集合的并、交、差等集合运算
  //设两个集合的位向量分别为B和B,则B | B ;B & B ;B ^ B
  //分别对应集合的并、交、对称差
  vector<vector<int> > res;
  int len = S.size();
  if (len == 0) return res;
  sort(S.begin(), S.end());
  int times = 1 << len;
  for (int i = 0; i < times; i++) {
    vector<int> path;
    for (int j = 0; j < len; j++) {
            if(i & (1 << j))    // important,这一位是1就取出来
                path.push_back(S[j]);
    }
    res.push_back(path);
  }
}

增量构造法,迭代,简洁:iteration increment

vector<vector<int> > subsets(vector<int> &S) {
  //增量迭代  时间复杂度 O(2^n),空间复杂度 O(1)
  sort(S.begin(), S.end());
  vector<vector<int> > res(1);  //先开辟1个的空间,must has one element, the []
  for (int i = 0; i < S.size(); i++) {
    for (int j = res.size() - 1; j >= 0; j--) {
      vector<int> path = res[j];
      path.push_back(S[i]);
      res.push_back(path);
    }
  }
  return res;
}

2. Subsets II - Leetcode 90

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],
  []
]

利用通用模板:

vector<vector<int> > subsetsWithDup(vector<int> &S) {
  sort(S.begin(), S.end());
  vector<vector<int> > res;
  vector<int> path;
  subsetsWithDupCore(S, res, path, 0);
  return res;
}
void subsetsWithDupCore(vector<int> &S, vector<vector<int> > &res,
                        vector<int> &path, int start) {
  res.push_back(path);
  for (int i = start; i < S.size(); i++) {
    // not i!=0,应该是i!=start,只有第一次进去的时候,放入,
    // 其他的情况都是,放第一个2和放第二个2一样的,只有数量的区别。也就是在同一个循环内,避免放同样的数字
    if (i != start && S[i] == S[i - 1]) continue;
        path.push_back(S[i]);
        subsetsWithDupCore(S, res, path, i+1);
        path.pop_back();
    }
}

其他方法:

1,二进制模拟 不适用

可以内部去重判断,if(find(res.begin(), res.end(), line) == res.end()) 这样也比较慢,没有下面的方法好

2,增量构造法,这个可以用,较难理解

vector<vector<int> > subsetsWithDup(vector<int> &S) {
  vector<vector<int> > res(1);
  sort(S.begin(), S.end());
  int len = 1;
  for (int i = 0; i < S.size(); i++) {
    int start = 0;  // record the same entrance
    //相同的数字,那么start就从上次开始的地方开始,上次start之前的,不能重复弄了!
    if (i != 0 && S[i] == S[i - 1])  start = len;
    len = res.size();  //更新len
    for (int j = start; j < len; j++) {
      vector<int> path = res[j];
      path.push_back(S[i]);
      res.push_back(path);
    }
  }
  return res;
}

3. Combinations

Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.
For example,
If n = 4 and k = 2, a solution is:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

利用通用模板,这是求2个的,若求所有的,时间O(2^n)

vector<vector<int> > combine(int n, int k) {
  vector<vector<int> > res;
  if ((n == 0) || (k == 0)) return res;
  vector<int> path;
  combineCore(n, k, 1, res, path);  // start from 1 to n
  return res;
}
void combineCore(int n, int k, int start, vector<vector<int> > &res, vector<int> &path) {
  if (path.size() == k) {
    res.push_back(path);
    return;
  }
  for (int i = start; i <= n; i++) {
    path.push_back(i);
    combineCore(n, k, i + 1, res, path);
    path.pop_back();
  }
}
Loading Disqus comments...
Table of Contents