77. 組合/78.子集/90. 子集 II

之前的全排列系列是有細節區別的,一直不理解。

  • 1.dfs(nums,res,out, i + 1);強調的是下一個狀態選或不選
  • 2.dfs(nums,res,out, start + 1);強調的是下一個子集解
    全排列系列
77. 組合

輸入: n = 4, k = 2
輸出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

在這裏插入圖片描述

 class Solution {
 public:
	 void help(vector<vector<int>>&res, vector<int>&out, int n, int k, int level)
	 {
		 if (out.size() == k) { res.push_back(out); return; }
		 for (int i = level; i <= n; i++)
		 {
			 out.push_back(i);
			 help(res, out, n, k, i + 1);
			 out.pop_back();
		 }
	 }
	 vector<vector<int>> combine(int n, int k) {
		 vector<vector<int>>res;
		 vector<int>out;
		 help(res,out,n,k,1);
         return res;
	 }
 };

78.子集

例如:nums=[1,2,3]
res:[[],[1],[1,2],[1,2,3],[1,3],[2],[2,3],[3]]

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

                        []        
                   /          \        
                  /            \     
                 /              \
              [1]                []
           /       \           /    \
          /         \         /      \        
       [1 2]       [1]       [2]     []
      /     \     /   \     /   \    / \
  [1 2 3] [1 2] [1 3] [1] [2 3] [2] [3] []
class Solution {
 public:
	 //回溯法,一系列全排列
	 void dfs(vector<int>& nums,vector<vector<int>>&res, vector<int>&out, int start)
	 {
		 res.push_back(out);//每個狀態都壓進去
		 for (int i = start; i < nums.size(); i++)
		 {
			 out.push_back(nums[i]);
			 dfs(nums,res,out, i + 1);
			 out.pop_back();
		 }
	 }
	 vector<vector<int>> subsets(vector<int>& nums) {
		 vector<vector<int>>res;
		 vector<int>out;          
		 dfs(nums,res,out,0);
		 return res;
	 }

 };
static const auto speedup=[](){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    return nullptr;
}();

假設nums[1,2,3]如果是寫成dfs(nums,res,out, start + 1);結果是

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

出現[2,2]原因是i已經到了1,但是還要遞歸start+1即1.

90. 子集 II

利用上面的代碼nums=[1,2,2],
其結果是[[],[1],[1,2],[1,2,2],[1,2],[2],[2,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的葉節點,因爲它們是重複的子集,應被拋棄。代碼如下:

class Solution {
public:
    
	 void dfs(vector<int>& nums,vector<vector<int>>&res, vector<int>&out, int start)
	 {
         			 
		 res.push_back(out);
		 for (int i = start; i < nums.size(); i++)
		 {
			 out.push_back(nums[i]);
			 dfs(nums,res,out, i + 1);
			 out.pop_back();
             //while(i!=0&&nums[i]==nums[i-1])i++;//已經添加了
             while((i!=nums.size()-1)&&nums[i]==nums[i+1])i++;
		 }
	 }
	 vector<vector<int>> subsetsWithDup(vector<int>& nums) {
		 vector<vector<int>>res;
		 vector<int>out;
         sort(nums.begin(),nums.end());
		 dfs(nums,res,out,0);
		 return res;
	 }
    

 };


寫成這樣更快:
不要後加入res,先加

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        vector<vector<int>> res ;
        int len = nums.size();
        sort(nums.begin(),nums.end());
        vector<int> temp; 
        res.push_back(temp);
        fun(nums,0,temp,res);
        return res;
    }
    void fun(vector<int>& nums,int i,vector<int>& temp,vector<vector<int>> &res){
        
        for(int j = i;j<nums.size();j++){
            if(j>i&&nums[j]==nums[j-1])
                continue;
            temp.push_back(nums[j]);
            res.push_back(temp);
            fun(nums,j+1,temp,res);
            temp.pop_back();
        }
        return;
        
    }
};

[1]https://blog.csdn.net/u010500263/article/details/18435495
[2]https://www.cnblogs.com/grandyang/p/4332522.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章