17 子集(遞歸)

描述

給定一個含不同整數的集合,返回其所有的子集

子集中的元素排列必須是非降序的,解集必須不包含重複的子集

您在真實的面試中是否遇到過這個題?  是

樣例

如果 S = [1,2,3],有如下的解:

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

挑戰

你可以同時用遞歸與非遞歸的方式解決麼?

遞歸的方法:
不要去想遞歸是如何一層一層調用的,而是要想如果解決當前問題,找到遞歸的出口

可以這樣思考,由於原集合每一個數字只有兩種狀態,要麼存在,要麼不存在,那麼在構造子集時就有選擇和不選擇兩種情況,所以可以構造一棵二叉樹,左子樹表示選擇該層處理的節點,右子樹表示不選擇,最終的葉節點就是所有子集合,樹的結構如下:

 

 

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

代碼如下:
 

class Solution {
public:
    /**
     * @param nums: A set of numbers
     * @return: A list of lists
     */
    vector<vector<int>> subsets(vector<int> &nums) {
        // write your code here
       //這裏排序並不必須,只是爲了子集中元素以遞增排序
        sort(nums.begin(), nums.end());
        vector<vector<int>> subs;
        vector<int> sub;  
        genSubsets(nums, 0, sub, subs);
        return subs; 
    }
    void genSubsets(vector<int>& nums, int start, vector<int>& sub, vector<vector<int>>& subs) {
        //我們發現這裏好像並沒有遞歸出口,因爲終止條件由下面for循環控制
        subs.push_back(sub);        
        for (int i = start; i < nums.size(); i++) {
            //情況一:子集包含元素nums[i]
            sub.push_back(nums[i]);
            //爲何是i+1,因爲已經解決了第i個元素,需要遞歸從第i+1個元素開始求解
            genSubsets(nums, i + 1, sub, subs);
            //情況二:子集不包含nums[i],即略過第i個元素
            //可以想象,不去管上一條遞歸語句,當下一次循環到i+1時,第i個元素已經略過
            sub.pop_back();
        }
    }
};

二:含有重複元素的子集合

For example,
If S = [1,2,2], a solution is:

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

思想:根據上面子集合 裏的構建樹的方法,在處理到第二個2時,由於前面已經處理了一次2,這次我們只在添加過2的[2] 和 [1 2]後面添加2,其他的都不添加,那麼這樣構成的二叉樹如下圖所示:

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

 

class Solution {
public:
    /**
     * @param nums: A set of numbers
     * @return: A list of lists
     */
    vector<vector<int>> subsets(vector<int> &nums) {
        // write your code here
       //這裏排序並不必須,只是爲了子集中元素以遞增排序
        sort(nums.begin(), nums.end());
        vector<vector<int>> subs;
        vector<int> sub;  
        genSubsets(nums, 0, sub, subs);
        return subs; 
    }
    void genSubsets(vector<int>& nums, int start, vector<int>& sub, vector<vector<int>>& subs) {
        //我們發現這裏好像並沒有遞歸出口,因爲終止條件由下面for循環控制
        subs.push_back(sub);        
        for (int i = start; i < nums.size(); i++) {
            //情況一:子集包含元素nums[i]
            sub.push_back(nums[i]);
            //爲何是i+1,因爲已經解決了第i個元素,需要遞歸從第i+1個元素開始求解
            genSubsets(nums, i + 1, sub, subs);
            //情況二:子集不包含nums[i],即略過第i個元素
            //可以想象,不去管上一條遞歸語句,當下一次循環到i+1時,第i個元素已經略過
            sub.pop_back();
          while(i+1<nums.size()&&nums[i]==nums[i+1])   ++i;
        }
    }
};

 

發佈了79 篇原創文章 · 獲贊 110 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章