代码随想录Day23 | 39.组合总和、40.组合总和II、131.分割回文串

39.组合总和

自己写的代码:

c 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;
    int sum=0;
    void backtracking(vector<int>& candidates,int target,int startIndex){
        if(sum>target) return;
        if(sum==target){
            res.push_back(path);
            return;
        }
        for(int i=startIndex;i<candidates.size();i++){
            path.push_back(candidates[i]);
            sum+=candidates[i];
            backtracking(candidates,target,i);
            sum-=candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtracking(candidates,target,0);
        return res;
    }
};

关键点:

  • 通过和来控制树的深度

剪枝优化:

剪枝通常要在for循环里面做文章,上面的代码,对于sum大于target的情况,其实也是进入了下一层递归,只是下一层递归会判断sum>target而直接返回。

如果已经知道下一层的sum会大于target,就没有必要进入下一层递归了

对总集合排序之后,如果下一层的sum(就是本层的 sum + candidatesi)已经大于target,就可以结束本轮for循环的遍历。

优化后代码:

c 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;
    int sum=0;
    void backtracking(vector<int>& candidates,int target,int startIndex){
        if(sum==target){
            res.push_back(path);
            return;
        }
        for(int i=startIndex;i<candidates.size()&&sum+candidates[i]<=target;i++){
            path.push_back(candidates[i]);
            sum+=candidates[i];
            backtracking(candidates,target,i);
            sum-=candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        backtracking(candidates,target,0);
        return res;
    }
};

40.组合总和II

自己AC的代码:

c 复制代码
class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    int sum=0;
    void backtracking(vector<int>& candidates, int target,int index){
        if(sum==target){
            res.push_back(path);
            return;
        }
        for(int i=index;i<candidates.size()&&sum+candidates[i]<=target;){
            path.push_back(candidates[i]);
            sum+=candidates[i];
            backtracking(candidates,target,i+1);
            sum-=candidates[i];
            path.pop_back();
            int j=i;
            while(i<candidates.size()&&candidates[j]==candidates[i]){
                i++;
            }
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        backtracking(candidates,target,0);
        return res;
    }
};

去重的逻辑(自己想的):通过画树辅助分析,在回溯的时候,弹出去的元素,与下一层循环要加进来的元素不能相等,这样答案就不会重复(当然最一开始先对数组进行排序)

所以for循环的条件不能简单加1,而是要通过循环找到第一个与当前元素不同的位置。

代码随想录讲解:

用一个bool数组来定义每个下标的元素是否使用过,我们要去重的维度是树层,而树枝上可以有重复元素(实质上是不同下标但数值相同的元素)

c 复制代码
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {
        if (sum == target) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
            // used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
            // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
            // 要对同一树层使用过的元素进行跳过
            if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
                continue;
            }
            sum += candidates[i];
            path.push_back(candidates[i]);
            used[i] = true;
            backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次
            used[i] = false;
            sum -= candidates[i];
            path.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<bool> used(candidates.size(), false);
        path.clear();
        result.clear();
        // 首先把给candidates排序,让其相同的元素都挨在一起。
        sort(candidates.begin(), candidates.end());
        backtracking(candidates, target, 0, 0, used);
        return result;
    }
};

个人觉得自己的思路更好理解。

131.分割回文串

c 复制代码
class Solution {
public:
    vector<vector<string>> res;
    vector<string> path;
    bool isHuiWen(string s,int i,int j){
        while(i<=j){
            if(s[i]!=s[j]) return false;
            i++;j--;
        }
        return true;
    }
    void backtracking(string s,int startIndex){
        if(startIndex>=s.size()){
            res.push_back(path);
            return;
        }
        for(int i=startIndex;i<s.size();i++){
            if(isHuiWen(s,startIndex,i)){
                string str=s.substr(startIndex,i-startIndex+1);
                path.push_back(str);
            }else{
                continue;
            }
            backtracking(s,i+1);
            path.pop_back();
        }
    } 
    vector<vector<string>> partition(string s) {
        backtracking(s, 0);
        return res;
    }
};
  1. 终止条件:当切割到最后一个位置时,结束。体现在代码中就是开始位置在最后一个元素之后
  2. 子串区间:子串的区间是[startIndex,i]
  3. 首先判断这个子串是不是回文,如果是回文,就加入在vector<string> path中,path用来记录切割过的回文子串。
相关推荐
Fuyo_11194 分钟前
C++ AVL树保姆级详解:从平衡因子到四大旋转机制
java·c++·算法
努力努力再努力wz15 分钟前
【Lua 入门系列】Lua 函数深入理解:函数对象、多返回值、可变参数、闭包、递归与尾调用优化
开发语言·lua
Sarvartha22 分钟前
Date、LocalDate、LocalTime、LocalDateTime、DateTimeFormatter类
java
jktantan24 分钟前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript
阿俊-全栈开发40 分钟前
LikeShop 多商户 Java 版商城微服务模块拆分与部署架构演进实践
java·微服务·架构·likeshop开源商城·多商户源码
揽星河..1 小时前
【C语言】猜数字游戏
c语言·算法·游戏
飞浪纪元[FWC–FE]1 小时前
从零到一键绿:Qt 5.14.2 + VS2019 开发环境搭建、Kit 套件配置与构建错误修复完全手册
开发语言·qt
Wang's Blog1 小时前
Java 项目实战:交友项目 - 联系人列表
java·开发语言·交友
我命由我123451 小时前
访客系统形式(有用户概念、无用户概念)
java·开发语言·前端·javascript·python·产品运营·产品经理
架构师伯子明1 小时前
FFmpeg C++学习(一):编译和demo
开发语言·c++·学习