代码随想录算法训练营第二十七天|●39. 组合总和 ● 40.组合总和II ● 131.分割回文串(JS写法)

39 组合总和

题目链接/文章讲解:https://programmercarl.com/0039.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C.html
视频讲解:https://www.bilibili.com/video/BV1KT4y1M7HJ
在这里插入图片描述
一开始自己写的大概和答案差不多,但是弄不明白回溯要传递的参数,但是自己一开始想到了终止条件,如果>7了就不对了,=7才对,<7就继续加
在这里插入图片描述

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum = function(candidates, target) {
    let res = [];
    let path = [];
    const backTracking = (start,sum) => {
        // let sum = 0;
        //终止条件
        if(sum >= target){
            if(sum === target){
                res.push(path.slice());            
            }
            return;
        }
        
        //单层循环
        for(let i = start;i<candidates.length;i++){
            path.push(candidates[i]);
            //如果把sum的计算写在外面,由于回溯,在下面也要撤销
            //也可以直接写成backTracking(i,sum+candidates[i]);
            sum += candidates[i];   
            // 基于此继续选择,传i,下一次就不会选到i左边的数(但会选到i,也就是会选到自己)
            //传i+1就不会选到自己了
            //从自己本身开始,不选到左边的数据,也是一种剪枝
            backTracking(i,sum);
            sum -= candidates[i]; 
            path.pop();
        }
    }
    backTracking(0,0);
    return res;

};

40 组合总和Ⅱ

题目链接/文章讲解:https://programmercarl.com/0040.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CII.html
视频讲解:https://www.bilibili.com/video/BV12V4y1V73A
在这里插入图片描述
本题一开始给的candidates可能会包含重复的元素,这一点我一开始忽视了,导致我以为和39题基本一致,只需要在for循环的的回溯用i+1即可,但是给出的答案是包含重复组合的。
这道题目和39.组合总和 (opens new window)如下区别:

本题candidates 中的每个数字在每个组合中只能使用一次。
本题数组candidates的元素是有重复的,而39.组合总和 (opens new window)是无重复元素的数组candidates
最后本题和39.组合总和 (opens new window)要求一样,解集不能包含重复的组合。

本题的难点在于区别2中:集合(数组candidates)有重复元素,但还不能有重复的组合。
这个去重为什么很难理解呢,所谓去重,其实就是使用过的元素不能重复选取。 这么一说好像很简单!

都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因。

那么问题来了,我们是要同一树层上使用过,还是同一树枝上使用过呢?

回看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。

所以我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。
强调一下,树层去重的话,需要对数组排序!
在这里插入图片描述
在这里插入图片描述
本题相较于39只需要改动以下三点:
在这里插入图片描述

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum2 = function(candidates, target) {
    let res = [];
    let path = [];
    candidates.sort((a,b) => a-b);
    const backTracking = (start,sum) => {
        //终止条件
        if(sum >= target){
            if(sum === target){
                res.push(path.slice());
                return;
            }
            return;
        }        
        //单词循环逻辑
        for(let i = start;i<candidates.length;i++){
            if (i - 1 >= start && candidates[i - 1] == candidates[i]) { // 当前选项和左邻选项一样,跳过
                continue;
            }
            path.push(candidates[i]);
            backTracking(i+1,sum+candidates[i]);
            path.pop();
        }
    }
    backTracking(0,0);
    return res;


};

131 分割回文串

https://programmercarl.com/0131.%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2.html
视频讲解:https://www.bilibili.com/video/BV1c54y1e7k6
在这里插入图片描述
在这里插入图片描述

/**
 * @param {string} s
 * @return {string[][]}
 */
 //判断是不是回文字符串
 const isPalindrome = (s,l,r) => {
    for(let i = l,j = r;i<j;i++,j--){
        if(s[i] !== s[j]) return false;
    }
    return true;
 }
var partition = function(s) {
    const res = [];
    const path = [];
    function backTracking(start){
        //终止条件,start代表切割符号,start走到最后了,那也就结束了
        if(start >= s.length){
            // res.push(Array.from(path));
            res.push(path.slice());
            return;
        }
        for(let i = start;i<s.length;i++){
            //需要判断的是[start,i]这个区间内截取的子串
            //如果不是回文子串则跳过本次循环
            if(!isPalindrome(s,start,i)) continue;
            path.push(s.slice(start,i+1));
            //寻找i+1为起始位置的子串
            //注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1
            backTracking(i+1);
            path.pop();
        }
    }
    backTracking(0);
    return res;
};
  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值