回溯之全排列Ⅰ、Ⅱ、子集Ⅰ、Ⅱ、组合 总结 C++/Python3

经典回溯算法模板

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

回溯之全排列Ⅰ、Ⅱ、子集Ⅰ、Ⅱ、组合 总结 C++/Python3_第1张图片
【通过flag标志位判断数组中某一元素是否已被包含】

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;
        if(nums.size()==0) return res;
        vector<int> arr;
        vector<bool> flag(nums.size(),false);  //标志位
        dfs(nums,flag,res,arr);
        return res;
    }
    void dfs(vector<int>& nums, vector<bool>& flag, vector<vector<int>>& res,vector<int>& arr){
        if(arr.size()==nums.size())
            res.push_back(arr);
            
        for(int i=0;i<nums.size();i++){
            if(flag[i]==false){   //更通用,尤其针对有重复元素,使用flag标志位
                flag[i]=true;   
                arr.push_back(nums[i]);
                
                dfs(nums,flag,res,arr);

                flag[i]=false;
                arr.pop_back();
            }
        }
    }
};

或写成【通过find函数判断数组中是否已经包含某一元素】:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums){
        vector<vector<int>> res;
        if(nums.size()==0) return res;
        vector<int> arr;
        dfs(nums,arr,res);
        return res;
    }
    void dfs(vector<int>& nums, vector<int>& arr, vector<vector<int>>& res){
        if(arr.size()==nums.size()){
            res.push_back(arr);
           // return;
        }
        for(int i=0;i<nums.size();i++){
            if(find(arr.begin(),arr.end(),nums[i])!=arr.end())   //针对无重复元素使用较好
                continue;
            arr.push_back(nums[i]);
            dfs(nums,arr,res);
            arr.pop_back();
        }
    }
};

回溯之全排列Ⅰ、Ⅱ、子集Ⅰ、Ⅱ、组合 总结 C++/Python3_第2张图片
【通过flag标志位判断数组中某一元素是否已被包含;并且flag也用于去重】

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> res;
        if(nums.size()==0) return res;
        sort(nums.begin(),nums.end());  //先排序,再剪枝
        vector<int> arr;
        vector<bool> flag(nums.size(),false);
        dfs(nums,flag,res,arr);
        return res;
    }
    void dfs(vector<int>& nums, vector<bool>& flag, vector<vector<int>>& res,vector<int>& arr){
        if(arr.size()==nums.size()) res.push_back(arr);
        for(int i=0;i<nums.size();i++){
            if(flag[i]==false){   //更通用,尤其针对有重复元素,使用flag标志位
                if(i>0 && nums[i]==nums[i-1] && flag[i-1]==true) continue;   //剪枝!!!去重
                
                flag[i]=true;   
                arr.push_back(nums[i]);
                dfs(nums,flag,res,arr);
                flag[i]=false;
                arr.pop_back();
            }
        }
    }
};

或直接用库函数:

class Solution {
public:
	vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>> res;
        if(nums.size()==0) return res;
        sort(nums.begin(),nums.end());
        res.push_back(nums);
        while(next_permutation(nums.begin(),nums.end()))
            res.push_back(nums);
        
        return res;
    }
};

回溯之全排列Ⅰ、Ⅱ、子集Ⅰ、Ⅱ、组合 总结 C++/Python3_第3张图片
【使用start保证数组中无重复元素】

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> arr;
        dfs(nums,0,arr,res);
        return res;
    }
    void dfs(vector<int>& nums,int start,vector<int>& arr, vector<vector<int>>& res){
        res.push_back(arr);

        for(int i=start;i<nums.size();i++){   //注意,此处i=start!  若i从0开始,则不能满足无重复子集
            arr.push_back(nums[i]);
            dfs(nums,i+1,arr,res);   //注意:此处i+1! 若为start+1,则会重复取值
            arr.pop_back();
        }
    }
};

回溯之全排列Ⅰ、Ⅱ、子集Ⅰ、Ⅱ、组合 总结 C++/Python3_第4张图片
【使用start保证数组中无重复元素】

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(),nums.end());  //先排序再剪枝
        vector<vector<int>> res;
        vector<int> arr;
        dfs(nums,0,res,arr);
        return res;
    }
    void dfs(vector<int>& nums,int start,vector<vector<int>>& res,vector<int>& arr){
        res.push_back(arr);

        for(int i=start;i<nums.size();i++){
                if(i>start && nums[i]==nums[i-1]) continue;   //剪枝!!去重

                arr.push_back(nums[i]);
                dfs(nums,i+1,res,arr);
                arr.pop_back();
        }
    }
};

回溯之全排列Ⅰ、Ⅱ、子集Ⅰ、Ⅱ、组合 总结 C++/Python3_第5张图片
【使用start保证数组中无重复元素】

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> res;
        if(n<0 || k<0) return res;
        vector<int> arr;
        dfs(n,k,1,res,arr);
        return res;
    }
    void dfs(int n,int k,int start,vector<vector<int>>& res,vector<int>& arr){
        if(arr.size()==k) 
        	res.push_back(arr);

        for(int i=start;i<=n;i++){
            arr.push_back(i);
            dfs(n,k,i+1,res,arr);
            arr.pop_back();
        }
    }
};

回溯之全排列Ⅰ、Ⅱ、子集Ⅰ、Ⅱ、组合 总结 C++/Python3_第6张图片

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        #回溯+剪枝
        res=[]
        #if not candidates: return res
        #candidates.sort()
        arr=[]
        self.dfs(candidates,target,arr,res,0)
        return res

    def dfs(self,candidates,target,arr,res,start):
        if target==0:
            #self.res.append(arr[:])    #注意:Python 中可变对象是引用传递,因此需要将当前 arr 里的值拷贝出来,或者使用 path.copy()
            res.append(arr.copy())   
            return

        for i in range(start,len(candidates)):
            if target<0:
                break

            arr.append(candidates[i])
            self.dfs(candidates,target-candidates[i],arr,res,i)
            arr.pop()

总结:
子集问题:关键点在于要用一个 start 参数排除已选择的数字。
组合问题:关键点在于要用一个 start 排除已经选择过的数字。
排列问题:关键点在于使用 flag标志位 或者 find方法 排除已经选择的数字
多观察递归树的结构!!!

你可能感兴趣的:(C++,回溯)