1. 程式人生 > 其它 >如何求一個集合所有的子集

如何求一個集合所有的子集

記求一個集合的所有子集的三種方法

來源:記求一個集合的所有子集的三種方法-zhyjc6's Blog

前言

今天刷 Leetcode 題目遇到一個求一個無重複元素陣列的全部子集,遇到這種題目如果是以前我可能會使用迭代法,首先將一個空陣列加入結果集,然後遍歷陣列中的元素,對於每個元素,遍歷結果集中的全部子集,向全部子集中加入當前元素得到新的子集,再將這些新的子集加入結果集。但現在我第一想到的不是這個解法,而是回溯法,因為回溯的意義就是找到所有可能的結果。並且回溯法寫起來給人的感覺特別優雅,又易讀易懂,掌握了之後感覺真的很好。

我寫好了之後一遍提交通過,和往常一樣我又來到了討論區,看到了官方題解的一個解法是利用二進位制數。我震驚了,這都能扯上關係?看到官方題解有這個方法,那麼國際版高贊一定也有這個解法,並且程式碼更簡潔,講解更易懂。於是我果然在高贊區看到了。這就是方法三

我們先看題目描述:

題目連結

解法一:普通迭代法

思路

  • 首先將空集加入結果集中,用作母體產生後面的結果。
  • 遍歷陣列,對於當前的元素
    • 遍歷之前結果集中的子集,將子集加入到結果集中,再將當前元素加入到尾部。

程式碼

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        res.push_back({});
        for (int num : nums) {
            int n = res.size();
            for (int i = 0; i < n; ++i) {
                res.push_back(res[i]);
                res.back().push_back(num);
            }
        }
        return res;
    }
};

複雜度

  • 時間複雜度:O(N∗2N)O(N∗2N) 。
  • 空間複雜度:O(N∗2N)O(N∗2N) 。

方法二:回溯法

思路

定義回溯函式,從start開始遍歷nums陣列中的元素,對於當前元素有兩種選擇:

  • 選擇加入結果集:那麼就從下一個元素開始呼叫回溯函式
  • 不加入結果集:什麼也不用做。

程式碼

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        backtrack(nums, 0);
        return res;
    }
private:
    vector<vector<int>> res;
    vector<int> tmp;
    void backtrack(vector<int> &nums, int start) {
        res.push_back(tmp);
        for (int i = start; i < nums.size(); ++i) {
            tmp.push_back(nums[i]);
            backtrack(nums, i+1);
            tmp.pop_back();
        }
    }
};

複雜度

  • 時間複雜度:O(N∗2N)O(N∗2N) 。
  • 空間複雜度:O(N∗2N)O(N∗2N) 。

方法三:二進位制法

思路

一個包含 n 個元素的集合的子集數量為 2n2n 。因為每個元素可以選擇選或者不選。深度利用這個規則,我們用二進位制數來表示每個元素的選或者不選。那麼我們需要一段長為n+1的二進位制數。因為我們需要的二進位制數範圍為:000…(n個0,表示全部不選,也就是空集) 到 111…(n個1,表示全選,也就是陣列本身)。因此我們的limit 就是總的子集數量。

從0遍歷到limit-1,看看當前的二進位制數,當前的二進位制數中的哪一位為1,就將nums陣列中的哪一位加入結果集中。就是這麼簡單!!!

程式碼

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        int n = nums.size(), limit = 1 << n;
        vector<vector<int>> res(limit);

        for (int i = 0; i < limit; ++i) {
            for (int j = 0; j < n; ++j) {
                if ((i >> j) & 1) {
                    res[i].push_back(nums[j]);
                }
            }
        }

        return res;
    }
};

複雜度

  • 時間複雜度:O(N∗2N)O(N∗2N) 。
  • 空間複雜度:O(N∗2N)O(N∗2N) 。