1. 程式人生 > >每天一道LeetCode-----找出給定序列的所有子序列

每天一道LeetCode-----找出給定序列的所有子序列

Subsets

這裡寫圖片描述

給定一個數組序列,找出所有子序列

深度優先掃一遍:)

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

這裡寫圖片描述
找到給定序列的所有子序列,給定的序列中可能會包含重複元素

解題時需要注意幾個地方

  • 容我好好吐槽一下,根本沒有說明好伐:(
  • 最後的結果中子序列的順序無要求,即[1,2,3]和[3,2,1]是相同的
  • 對於重複元素,第二條規定尤為重要,即[4,4,4,1]和[4,4,1,4]是相同的
  • 更重要的是,對於第三條,[1,4,4,4]和[4,4,4,1]以及[4,4,1,4]同樣是相同的

用[1,2,3]代替[3,2,1]以及用[1,4,4,4]代替[4,4,1,4]是什麼概念,就是說所有子序列可以都是遞增的,再往上想就是可以事先對給定序列排序,那解決重複問題就簡單多了

在Subsets中,通過深度優先找到了所有的子序列,但是如果序列中有重複元素,需要新增幾個限制條件

以序列[4,4,4,1,4]舉例,如果不新增限制條件,那麼最後的結果可能存在

[4,4,4,1],[4,4,1,4]以及[4,1],[1,4]以及[4,4,4],[4,4,4]

毫無疑問上面二個都是相同的,即結果中出現重複元素,不符合要求

深度優先和回溯在解決重複問題時通常是在下次遞迴之前判斷當前要新增到結果集中的元素是否應該被新增到結果集中,針對Subsets的模板


class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        //std::sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        vector<int> cur;
        dfs(nums, 0, cur, res);
        return res;
    }
private:
    void dfs(vector<int>& nums, int i, vector<int>& cur, vector<vector<int>>& res)
    {
        res.emplace_back(cur);
        for(int j = i; j != nums.size(); ++j)
        {
            //判斷是否應該新增到結果集中
            if(...)
            {
                cur.emplace_back(nums[j]);
                dfs(nums, j + 1, cur, res);
                cur.pop_back();        
            }
        }
    }
};

方法就是判斷nums[j]是否在[i : j-1]這個範圍內出現過,考慮當前遍歷到[1,4,4,4,4] (已排序)的下標1(即元素4的位置),在回溯之後遍歷的下標改為2(即第二個元素4的位置),這就會重複

因為在遍歷第一個4時進入深度優先遞迴,在遞迴的過程中已經將所有組合可能都記錄到結果中,其中就包括下標組合[1,3,4],那和第二次遞迴的下標組合[2,3,4]其實是一樣的,結果都是[4,4,4]

原因是對於任何重複的元素,只需要考慮第一個即可,假設i, i+1, i+2, …, k是重複元素,那麼選擇nums[i]和選擇nums[i+2]是一樣的,因為選擇nums[i]時,可以假設遞迴時不選擇nums[i+1],那麼就和直接選擇nums[i+2]一樣了

程式碼如下


class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        std::sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        vector<int> cur;
        dfs(nums, 0, cur, res);
        return res;
    }
private:
    void dfs(vector<int>& nums, int i, vector<int>& cur, vector<vector<int>>& res)
    {
        res.emplace_back(cur);
        for(int j = i; j != nums.size(); ++j)
        {
            if(j == i || nums[j] != nums[j - 1])
            {
                cur.emplace_back(nums[j]);
                dfs(nums, j + 1, cur, res);
                cur.pop_back();
            }
        }
    }
};