LeetCode|回溯算法|39. 组合总和 、 40. 组合总和 II 、131. 分割回文串

目录

一、39. 组合总和

1.题目描述

2.解题思路

3.代码实现

二、40. 组合总和 II

1.题目描述

2.解题思路

3.代码实现

三、131. 分割回文串

1.题目描述

2.解题思路

3.代码实现


一、39. 组合总和

1.题目描述

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

2.解题思路

关键点1:如何确定终止条件?

  • 当收集结果的path数组元素总和等于target的时候,就是收集结果的时候。

关键点2:如何确定单层递归逻辑?

  • 首先我们将元素push到path中,然后更新一下sum总和
  • 然后,在深度递归时候,不需要startIndex+1,(因为这里元素可以重复选取,所以直接从i开始就行)
  • 最后,就是回溯操作。将path中的元素pop,更新sum。

3.代码实现

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;//结果集
    void backtracking(vector<int>& candidates,int target,int sum,int startIndex){
        if(sum > target)
            return;//剪枝操作
        //终止条件
        if(sum == target){
            //收集结果
            result.push_back(path);
            return;
        }
        //单层逻辑
        for(int i = startIndex;i < candidates.size();i++){
            path.push_back(candidates[i]);
            sum += candidates[i];
            backtracking(candidates,target,sum,i);
            //回溯操作
            sum -= candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtracking(candidates,target,0,0);
        return result;
    }
};

二、40. 组合总和 II

1.题目描述

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

2.解题思路

关键在于去重:先对所给数组排序,让相同元素在一起,没有必要让重复的元素都作为组合的开头元素。

  • 例如所给数组:1、1、4、5、6、7;target = 8。符合条件的一组组合有 1  7 。如果以第一个1作为开头的组合,那么在以第二个1为开头时,就需要做去重操作。

3.代码实现

class Solution {
public:
    vector<int> path;
    vector<vector<int>> result;
    void backtracking(vector<int>& candidates,int target,int sum,int startIndex){
        //终止条件
        if(sum > target)
            return;
        if(sum == target){
            result.push_back(path);
            return;
        }
        //单层逻辑
        for(int i = startIndex;i < candidates.size();i++){
            //树层去重
            if(i > startIndex && candidates[i] == candidates[i - 1]){
                continue;
            }
            path.push_back(candidates[i]);
            sum += candidates[i];
            backtracking(candidates,target,sum,i + 1);
            //回溯操作
            path.pop_back();
            sum -= candidates[i];
        }
        
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        //先对candidates排序,让相同元素靠在一起
        sort(candidates.begin(),candidates.end());
        backtracking(candidates,target,0,0);
        return result;
    }
};

三、131. 分割回文串

1.题目描述

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。

回文串 是正着读和反着读都一样的字符串。

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

输入:s = "a"
输出:[["a"]]

2.解题思路

        分隔子串,其实和组合问题大同小异,例如 “aab”,取第一个a,相当于在a后面加了分割线,取第二个a,相当于在aa后面加了分割线。

        关键点1:如何确定终止条件?       

  • 如果分隔线也就是startIndex到了题目所给字符串的末尾甚至更后面, 就停止,收集结果。

        关键点2:单层逻辑怎么确定?       

  • 首先,需判断所分隔的子串是否为回文子串,如果是,就添加到path数组中,否则continue。这里的难点是,如何描述这个分割的子串:[startIndex,i]。
  • 其次,进入深度递归,起始位置startIndex需要+1
  • 最后,进入回溯操作,path.pop_back()    

3.代码实现

class Solution {
public:
    //编写函数,判断是否为回文数
    bool isHuiWenChuan(string s,int start,int end){//遵循左闭右闭
        int i = start,j = end;
        for(;i < j;i++,j--){
            if(s[i] != s[j])
                return false;
        }
        //遍历结束,return true
        return true;
    }
    vector<string> path;//存放符合条件的路径
    vector<vector<string>> result;//结果集
    void backtracking(string s,int startIndex){
        //判断终止条件
        if(startIndex >= s.size()){//起始点为字符串最后面了,没法继续切割了,就收集结果
            //对路径是否符合题目要求的判断语句 写在单层循环里
            result.push_back(path);
            return;
        }
        //单层逻辑
        for(int i = startIndex;i < s.size();i++){
            //判断[startIndex,i]之间 是否为回文子串
            if(isHuiWenChuan(s,startIndex,i)){
                //如果是,就将子串存放到path中
                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 result;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值