zoukankan      html  css  js  c++  java
  • LeetCode Subsets I& II——递归

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

    这道求子集合的问题,由于其要列出所有结果,按照以往的经验,肯定要是要用递归来做。这道题其实它的非递归解法相对来说更简单一点,下面我们先来看非递归的解法,由于题目要求子集合中数字的顺序是非降序排列的,所有我们需要预处理,先给输入数组排序,然后再进一步处理,最开始我在想的时候,是想按照子集的长度由少到多全部写出来,比如子集长度为0的就是空集,空集是任何集合的子集,满足条件,直接加入。下面长度为1的子集,直接一个循环加入所有数字,子集长度为2的话可以用两个循环,但是这种想法到后面就行不通了,因为循环的个数不能无限的增长,所以我们必须换一种思路。我们可以一位一位的网上叠加,比如对于题目中给的例子[1,2,3]来说,最开始是空集,那么我们现在要处理1,就在空集上加1,为[1],现在我们有两个自己[]和[1],下面我们来处理2,我们在之前的子集基础上,每个都加个2,可以分别得到[2],[1, 2],那么现在所有的子集合为[], [1], [2], [1, 2],同理处理3的情况可得[3], [1, 3], [2, 3], [1, 2, 3], 再加上之前的子集就是所有的子集合了,代码如下:

     解法一:

     1 // Non-recursion
     2 class Solution {
     3 public:
     4     vector<vector<int> > subsets(vector<int> &S) {
     5         vector<vector<int> > res(1);
     6         sort(S.begin(), S.end());
     7         for (int i = 0; i < S.size(); ++i) {
     8             int size = res.size();
     9             for (int j = 0; j < size; ++j) {
    10                 res.push_back(res[j]);
    11                 res.back().push_back(S[i]);
    12             }
    13         }
    14         return res;
    15     }
    16 };

    整个添加的顺序为:

    []
    [1]
    [2]
    [1 2]
    [3]
    [1 3]
    [2 3]
    [1 2 3]

    下面来看递归的解法,相当于一种深度优先搜索,参见网友JustDoIt的博客,由于原集合每一个数字只有两种状态,要么存在,要么不存在,那么在构造子集时就有选择和不选择两种情况,所以可以构造一棵二叉树,左子树表示选择该层处理的节点,右子树表示不选择,最终的叶节点就是所有子集合,树的结构如下:

    []
    /
    /
    /
    [1] []
    / /
    / /
    [1 2] [1] [2] []
    / / / /
    [1 2 3] [1 2] [1 3] [1] [2 3] [2] [3] []

     1 // Recursion
     2 class Solution {
     3 public:
     4     vector<vector<int> > subsets(vector<int> &S) {
     5         vector<vector<int> > res;
     6         vector<int> out;
     7         sort(S.begin(), S.end());
     8         getSubsets(S, 0, out, res);
     9         return res;
    10     }
    11     void getSubsets(vector<int> &S, int pos, vector<int> &out, vector<vector<int> > &res) {
    12         res.push_back(out);
    13         for (int i = pos; i < S.size(); ++i) {
    14             out.push_back(S[i]);
    15             getSubsets(S, i + 1, out, res);
    16             out.pop_back();
    17         }
    18     }
    19 };

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

    这道子集合之二是之前那道 Subsets 子集合 的延伸,这次输入数组允许有重复项,其他条件都不变,只需要在之前那道题解法的基础上稍加改动便可以做出来,我们先来看非递归解法,拿题目中的例子[1 2 2]来分析,根据之前 Subsets 子集合 里的分析可知,当处理到第一个2时,此时的子集合为[], [1], [2], [1, 2],而这时再处理第二个2时,如果在[]和[1]后直接加2会产生重复,所以只能在上一个循环生成的后两个子集合后面加2,发现了这一点,题目就可以做了,我们用last来记录上一个处理的数字,然后判定当前的数字和上面的是否相同,若不同,则循环还是从0到当前子集的个数,若相同,则新子集个数减去之前循环时子集的个数当做起点来循环,这样就不会产生重复了,代码如下:

     1 // Non-recursion
     2 class Solution {
     3 public:
     4     vector<vector<int> > subsetsWithDup(vector<int> &S) {
     5         vector<vector<int> > res(1);
     6         sort(S.begin(), S.end());
     7         int size = 1, last = S[0];
     8         for (int i = 0; i < S.size(); ++i) {
     9             if (last != S[i]) {
    10                 last = S[i];
    11                 size = res.size();
    12             }
    13             int newSize = res.size();
    14             for (int j = newSize - size; j < newSize; ++j) {
    15                 res.push_back(res[j]);
    16                 res.back().push_back(S[i]);
    17             }
    18         }
    19         return res;
    20     }
    21 };

    对于递归的解法,根据之前 Subsets 子集合 里的构建树的方法,在处理到第二个2时,由于前面已经处理了一次2,这次我们只在添加过2的[2] 和 [1 2]后面添加2,其他的都不添加,那么这样构成的二叉树如下图所示:

    []
    /
    /
    /
    [1] []
    / /
    / /
    [1 2] [1] [2] []
    / / / /
    [1 2 2] [1 2] X [1] [2 2] [2] X []

    代码只需在原有的基础上增加一句话,while (S[i] == S[i + 1]) ++i; 这句话的作用是跳过树中为X的叶节点,因为它们是重复的子集,应被抛弃。代码如下:

     1 // Recursion
     2 class Solution {
     3 public:
     4     vector<vector<int> > subsetsWithDup(vector<int> &S) {
     5         vector<vector<int> > res;
     6         vector<int> out;
     7         sort(S.begin(), S.end());
     8         getSubsets(S, 0, out, res);
     9         return res;
    10     }
    11     void getSubsets(vector<int> &S, int pos, vector<int> &out, vector<vector<int> > &res) {
    12         res.push_back(out);
    13         for (int i = pos; i < S.size(); ++i) {
    14             out.push_back(S[i]);
    15             getSubsets(S, i + 1, out, res);
    16             out.pop_back();
    17             while (S[i] == S[i + 1]) ++i;
    18         }
    19     }
    20 };
  • 相关阅读:
    fzuoj Problem 2177 ytaaa
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Capture the Flag
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Team Formation
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Beauty of Array
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Lunch Time
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Convert QWERTY to Dvorak
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest May Day Holiday
    zoj The 12th Zhejiang Provincial Collegiate Programming Contest Demacia of the Ancients
    zjuoj The 12th Zhejiang Provincial Collegiate Programming Contest Ace of Aces
    csuoj 1335: 高桥和低桥
  • 原文地址:https://www.cnblogs.com/zl1991/p/7058925.html
Copyright © 2011-2022 走看看