频率次高的笔试题---回溯法类型(上)

1、组合总和

1.1数字可以重复选取

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合(candidates 中的数字可以无限制重复被选取)

示例1
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

思路: 回溯法法,如果这条路走不下去,就退回一级,重新选择路径,一直往下走,直到 target 等于 0 就添加到返回列表中,如果 target 小于 0 则返回

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        if(candidates.size()==0){
            return res;
        }
        combinationSum(candidates, target,0);
        return res;
    }
    void combinationSum(vector<int>& candidates,int target,int index){
        if(target < 0){
            return;
        }
        if(target == 0){
            res.push_back(temp);
        }
        for(int i = index;i<candidates.size();++i){
            temp.push_back(candidates[i]);
            combinationSum(candidates, target-candidates[i],i);
            temp.pop_back();
        }
    }
private:
    vector<vector<int>>res;
    vector<int>temp;
};
1.2数字不可以重复选取

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

思路:同样也是回溯法,只不过每个元素只能使用一次,所以我们先得排序,按照升序或者降序的方式,在满足一个条件之后进行判断前一个元素和后一个元素是否相等,如果相等就跳过去重

注意:这里说的去重并不是每个值是唯一的,而是给定数组中的值只能使用一次

class Solution {
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        if(candidates.empty()){
            return res;
        }
        sort(candidates.begin(),candidates.end());
        combinationSum2(candidates,target,0);
        return res;
    }
    void combinationSum2(vector<int>& candidates, int target,int index){
        if(target<0){
            return;
        }
        if(target == 0){
            res.push_back(temp);
            return;
        }
        for(int i = index;i<candidates.size();++i){
        	// 这个 if 语句还是挺难理解的,
        	// 目的防止在 candidates 中有重复的值被重复计算
        	// 而这个 if 语句在回溯的时候才会去判断,插入的时候不会判断
        	if(i>index && candidates[i] == candidates[i-1]){
                continue;
            }
            // 下面这个 if 语句加不加无所谓,加上程序跑的快
            // 如果当前的 candidates[i] 都已经大于 target 了
            // 就说明这条路肯定不行,需要退回去
 			if(candidates[i]>target){
                return;
            }
            temp.push_back(candidates[i]);
            combinationSum2(candidates,target-candidates[i],i+1);
            temp.pop_back();
        }
    }
private:
    vector<vector<int>>res;
    vector<int>temp;
};
1.3找出所有相加之和为 n 的 k 个数

每组数字只能含有 1- 9,不存在重复数字

class Solution {
public:
    vector<vector<int>> combinationSum3(int k, int n) {
        if(k > n){
            return ans;
        }
        dfs(k,n,1);
        return ans;
    }
    void dfs(int k,int n,int index){
        if(k < 0 || n < 0){
            return;
        }
        if(k == 0 && n == 0){
            ans.push_back(tmp);
        }
        for(int i = index; i<10;++i){
            tmp.push_back(i);
            dfs(k-1, n-i ,i + 1);
            tmp.pop_back();
        }
        return;
    }
private:
    vector<vector<int>>ans;
    vector<int>tmp;
};

2、全排列

2.1 返回数字的序列

给定一个没有重复数字的序列,返回其所有可能的全排列。

示例
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

解题思路:回溯法,在每一次的遍历都需要进行标记,如果访问了这个元素就标为 1,弹出这个元素就标 0 ,为的就是回溯的时候可以不重复

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int>used(nums.size(),0);
        dfs(used,nums);
        return res;
    }
    void dfs(vector<int>&used,vector<int>& nums){
        if(temp.size() == nums.size()){
            res.push_back(temp);
            return;
        }
        for(int i = 0;i<nums.size();++i){
            if(used[i] !=0){
                continue;
            }
            temp.push_back(nums[i]);
            used[i] = 1;
            dfs(used,nums);
            temp.pop_back();
            used[i] = 0;
        }
    }
private:
    vector<vector<int>>res;
    vector<int>temp;
};

方法二:交换的两个位置进行插入,然后再交换回去

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        if(nums.size() == 0){
            return ans;
        }
        dfs(nums,0,nums.size()-1);
        return ans;
    }
    void dfs(vector<int>&nums,int begin,int end){
        if(begin == end){
            ans.push_back(nums);
        }
        for(int i = begin;i<end + 1;++i){
            swap(nums[i],nums[begin]);
            dfs(nums,begin+1,end);
            swap(nums[i],nums[begin]);
        }
        return;
    }
private:
    vector<vector<int>>ans;
};
2.2 字母大小写排列

给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。

输入 S = “a1b2”
输出: [“a1b2”, “a1B2”, “A1b2”, “A1B2”]

class Solution {
public:
    vector<string> letterCasePermutation(string S) {
        vector<string>ans;
        backTrack(ans,S,0);
        return ans;
    }
private:
    void backTrack(vector<string>&ans,string S,int index){
        if(index == S.size()){
            ans.push_back(S);
            return;
        }
        // 不是字母的话
        if(!isalpha(S[index])){
            backTrack(ans,S,index+1);
        }
        else{
            S[index] = tolower(S[index]);
            backTrack(ans,S,index+1);
            S[index] = toupper(S[index]);
            backTrack(ans,S,index+1);
        }
    }
};

3、部分排列

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合

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

class Solution {
public:
    vector<vector<int>>ans;
    vector<int>tmp;
    vector<vector<int>> combine(int n, int k) {
        dfs(n,k,0);
        return ans;
    }
    void dfs(int n,int k,int index){
        if(tmp.size() == k){
            ans.push_back(tmp);
            return;
        }
        for(int i = index+1;i < n+1;++i){
            tmp.push_back(i);
            dfs(n,k,i);
            tmp.pop_back();
        }
    }
};

3、子集

3.1 不含重复元素数组

方法一:回溯法

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

方法二:利用位移思想

	vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>>ans; 
        int n = nums.size();
        int size = 1 <<n;
        for(int i = 0;i<size;++i){
            vector<int>tmp;
            for(int j = 0;j<n;++j){
                if(i & (1 <<j)){
                    tmp.push_back(nums[j]);
                }
            }
            ans.push_back(tmp);
        }   
        return ans;
    }
3.2 含有重复元素数组

核心思想是进行去重

class Solution {
public:
    vector<vector<int>>ans;
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<int>tmp;
        ans.push_back(vector<int>());
        dfs(0,nums,tmp);
        return ans;
    }
    void dfs(int start,vector<int>&nums,vector<int>&tmp){
        if(start >= nums.size()){
            return;
        }
        for(int i = start;i<nums.size();++i){
        	// 进行去重
            if(i != start && nums[i] == nums[i-1]){
                continue;
            }
            tmp.push_back(nums[i]);
            ans.push_back(tmp);
            dfs(i+1,nums,tmp);
            tmp.pop_back();
        }
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿的温柔香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值