代码随想录算法训练营day26| 39. 组合总和、40.组合总和II、131.分割回文串

代码随想录训练营】【Day26休息】【Day27】第七章|回溯算法39. 组合总和40.组合总和II131.分割回文串 阅读详情

代码随想录算法训练营day26| 39. 组合总和、40.组合总和II、131.分割回文串

39.组合总和

题目链接:39. 组合总和 - 力扣(Leetcode)
遇到问题

剪枝优化的方式没想到

题解

与昨日的组合综合III比较,其实多的只是一个可重复,因此在传入遍历开始的位置时,需要额外注意,其余无较大差别

根据回溯三部曲

1. 递归函数和传参

与之前一样肯定需要两个数组来保存遍历的结果,而对于参数,需要记录每次遍历的开始位置

let sum: number = 0
let path: number[] = []
let result: number[][] = []
function backtracking(candidates: number[],target: number, start: number): void
2. 递归终止条件

这里只要达到目标和就成立,因此很容易得出条件

// 剪枝
if(sum > target) return
if(sum == target){
    result.push([...path])
    return 
}
3.单层遍历

由于出现数组数目是可以重复的,因此,在传入strat时,不再是i + 1i,根据之前做的得出

for(let i = start; i < candidates.length; i++){
    sum += candidates[i]
    path.push(candidates[i])
    backtracking(candidates, target, start, i)
    sum -= candidates[i]
    path.pop()
}
整体代码

整体TS代码如下

function combinationSum(candidates: number[], target: number): number[][] {
    let sum: number = 0
    let path: number[] = []
    ley result: number[][] = []
    function backtracking(candidates: number[], target: number, start: number): void{
        if(target > sum) return 
        if(traget == sum) {
            result.push([...path])
            return
        }
        for(let i = start; i < candidates.length; i++){
        	sum += candidates[i]
            path.push(candidates[i])
            backtracking(candidates, target, i)
            sum -= candidates[i]
            path.pop()
        }
    }
    backtracking(candidates, target, 0)
    return result
}
剪枝

39.组合总和

对于sum已经大于target的情况,其实是依然进入了下一层递归,只是下一层递归结束判断的时候,会判断sum > target的话就返回。

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

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

function combinationSum(candidates: number[], target: number): number[][] {
    let sum: number = 0
    let path: number[] = []
    ley result: number[][] = []
    function backtracking(candidates: number[], target: number, start: number): void{
        if(traget == sum) {
            result.push([...path])
            return
        }
        // 剪枝优化部分
        for(let i = start; i < candidates.length && sum + candidates[i] <= target; i++){
        	sum += candidates[i]
            path.push(candidates[i])
            backtracking(candidates, target, i)
            sum -= candidates[i]
            path.pop()
        }
    }
    backtracking(candidates, target, 0)
    return result
}

40.组合总和II

题目链接:40. 组合总和 II - 力扣(Leetcode)
遇到问题

没注意题目中数组中有重复元素,然后没想到当重复元素遍历时会产生相同的枝段

题解
1.递归函数和传参

39.组合总和一样确认参数(sum也可以进行传递,但是为了方便的简洁,就不传了)

let sum: number = 0
let path: number[] = []
let result: number[][] = []
function backtracking(candidates: number[], target: number, start: number): void
2.递归终止条件

同样的有

if(sum == target){
    result.push([...path])
    return
}
3.单层遍历

对于单层需要注意的有两点

  1. 去除可能重复的情况分支
  2. 每个每个数字在每个组合中只能使用 一次

因此有

for(let i = start; i < candidates.length && sum + candidates[i] <= target; i++){
    if(i > start && candidates[i] == candidates[i - 1]){
        continue
    }
    sum += candidates[i]
    path.push(candidates[i])
    backtracking(candidates, target, i + 1)
    sum -= candidates[i]
    path.pop()
}
整体代码

整体TS代码如下

function combinationSum2(candidates: number[], target: number): number[][] {
    candidates.sort((a,b) => a - b)
    let sum: number = 0
    let path: number[] = []
    let result: number[][] = []
    function backtracking(candidates: number[], target: number, start: number): void{
        if(sum == target){
            result.push([...path])
            return
        }
        for(let i = start; i < candidates.length && sum + candidates[i] <= target; i++){
            if(i > start && candidates[i] == candidates[i - 1]){
                continue
            }
            sum += candidates[i]
            path.push(candidates[i])
            backtracking(candidates, target, i + 1)
            sum -= candidates[i]
            path.pop()
        }
    }
    backtracking(candidates, target, 0)
    return result
}

31.分割回文串

题目链接:131. 分割回文串 - 力扣(Leetcode)
遇到问题
  1. 对于字符串的分割不清晰
  2. 对于回文判断不明确
题解

切割思想如图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bR1ECuER-1675932889440)(https://code-thinking.cdn.bcebos.com/pics/131.%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2.jpg)]

懂得这个思想之后,会发现其主要方式和组合题目类似,然后可以利用回溯三部曲

1.递归函数和传参

与之前一样,需要两个数组记录结果,对于递归函数,则需要传入开始位置,和目标字符串

let path: string[] = []
let result: string[][] = []
function backtracking(s: string, start: number): void
2.递归终止条件

当切割到字符串的末尾的时候,也就是strat == s.length时,即为叶子节点,需要处理返回

 if(start >= s.length){
    result.push([...path])
    return
}
3.单层遍历
for(let i = start; i < s.length; i++){
    if(!isPalindrome(s,start,i)){
        continue
    }
     // 获取[start,i]在s中的子串
    path.push(s.substring(start, i + 1))
    backtracking(s, i + 1)
    path.pop()
}
回文串判断

现在来书写如何判断回文串

显然的根据回文串定义,我们只需要一个双指针,遍历整个字符串即可完成判断

function isPalindrome(s: string, start: number, end: number): boolean{
     for (let i = start, j = end; i < j; i++, j--) {
        if (s[i] != s[j]) {
            return false;
        }
    }
    return true;
}
总体代码

TS整体代码如下

function partition(s: string): string[][] {
    let path: string[] = []
    let result: string[][] = []
    function backtracking(s: string, start: number): void{
        if(start >= s.length){
            result.push([...path])
            return
        }
        for(let i = start; i < s.length; i++){
            if(!isPalindrome(s,start,i)){
                continue
            }
            path.push(s.substring(start, i + 1))
            backtracking(s, i + 1)
            path.pop()
        }
    }
    backtracking(s,0)
    return result
}

function isPalindrome(s: string, start: number, end: number): boolean{
     for (let i = start, j = end; i < j; i++, j--) {
        if (s[i] != s[j]) {
            return false;
        }
    }
    return true;
}
代码随想录算法训练营day26|39. 组合总和40.组合总和II131.分割回文串 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。思路:本题搜索的过程抽象成树形结构如下:注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!如果是一的话,就需要startIndex;如果是,各个集合之间相互不影响,那么就不用startIndex。 阅读详情

相关推荐

代码随想录算法训练营Day26】● 39. 组合总和;● 40.组合总和II;● 131.分割回文串

剪枝条件:当剩余备选数都比需要的数小时就不用遍历了。注意必须按顺序输出所以用队列不能用栈。先用自己和自己组合,直到超出。与39几乎一模一样,区别在于。

Tammy2001的博客 1035

代码随想录算法训练营Day26|39. 组合总和40.组合总和II131.分割回文串

这道题目的意思就是让你在这个数组中找元素,让其和等于target,然后数组里的元素可以被重复找,也就是说一个元素可以被找很多次,这里只要将递归的条件,也就是传入的参数从i+1改成i就可以了,然后同时注意,递归的终止条件就是当我的sum大于我i的target时,就要return,然后在每次递归结束后将数组里的元素弹出就可以了,来看具体代码的实现。这就是今天的题目,其实都是大差不差的,模板都是一样的回溯三部曲,多想,多练,多琢磨,我一定会成为一名优秀的程序员!今天相互要学习的是回溯算法的内容。

2401_82757749的博客 999

代码随想录算法训练营Day26 || leetCode 39. 组合总和 || 40.组合总和II || 131.分割回文串

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。结合前面几道组合的题目,这道题还是很好理解的,注意好结束条件和元素和重复选择的条件即可。看似和前面的题相差不多,其实要注重的细节很多,参考代码随想录的思路来解题就比较清晰。说明: 所有数字(包括目标数)都是正整数。

were_I_you的博客 525

代码随想录算法训练营第23期day26|39. 组合总和40.组合总和II131.分割回文串

这道题的主要步骤可以拆分成两个部分,如何分割和如何判断。基本的回溯方法遵循回溯模板,需要注意的是回溯中的startIndex是从当前i继续而不是i+1,因为组合中的数字允许重复。在基本回溯的过程中,终止条件是当前层的sum大于target,但如果之后的数比当前数大,那么后续的递归是多余的。但是这道题还有另一个难点,那就是数组中会出现重复的元素,同一位置的元素不能重复使用,同时最终答案也不能出现重复path,这就是一个判断树层去重(而不是树枝去重)的过程,为此加入一个used数组作为辅助判断。

weixin_42179093的博客 234

代码随想录算法训练营Day 26 || 39. 组合总和40.组合总和II131.分割回文串

然后,我们回溯到上一步,尝试下一个数(比如6、7等),但是因为这些数都会使得当前和大于目标和,所以我们可以直接结束搜索。然后,我们回溯到上一步,尝试下一个数(比如2、7等),但是因为这些数都会使得当前和大于目标和,所以我们可以直接结束搜索。通过这样的过程,我们最终可以找到所有可能的组合:[[1, 1, 6], [1, 2, 5], [1, 7], [2 ,6]]。这次,因为起始索引已经到达字符串的末尾,所以我们将当前路径(即 [“a”, “a”, “b”])添加到结果列表中。

m0_66462858的博客 107

代码随想录算法训练营 Day26 | LeetCode39. 组合总和LeetCode40.组合总和IILeetCode131.分割回文串

该题与之前的题相比涉及两个重要思路:1、去重:遍历当前元素时,重复回溯,可以再包含当前元素,但是不能再包含上一个元素,否则就会有重复,因此这里回溯参数选取的是i,而不是i+1;2、剪枝:因为数组不是有序的,如果按以前的方法剪枝,则会少掉很多结果,如果不排序,那么则需要在终止条件上加入return的逻辑,如果排序,则就可以在for循环里面剪枝。代码如下:最坏时间复杂度(n*2^n);空间复杂度O(targer)

ckw_lztxdy的博客 502

代码随想录算法训练营day26 | 39.组合总和 40.组合总和II 131.分割回文串

回溯

baimaozi_007的博客 197

代码随想录算法训练营Day27 (Day26休息) | 回溯算法(3/6) LeetCode 39. 组合总和 40.组合总和II 131.分割回文串

开始第三天的练习,阳了以后的发烧,嗓子疼全经历过了,不过学习还要继续!

记录学习每一天! 96

代码随想录算法训练营第 23 天 |LeetCode 39. 组合总和 LeetCode 40.组合总和II LeetCode131.分割回文串

回溯可以看成N叉树树的高度是递归深度,每一个树枝是一个路径(组合)树的宽度是for循环遍历,选择不同的起点。

2301_79647020的博客 890

代码随想录算法训练营day26

比如示例中排序后为[1,1,2,5,6,7,10],当遇到连续几个相同的数时,我想到的方法是此时begin无论从哪里开始,第一次都要把这几个数都选进去,比如第一次for循环,begin=1,这里第二个1也要选入path,然后从第三个数(第一个不是1的数)开始递归;当第二次for循环,begin=2的时候,即可正常运行,这样就可以完成去重。时,即说明出现了重复的元素,此时要判断i-1位置的used值,如果为1,则说明已经被选取,是树枝上的元素,可以重复,如果为0,说明为数层上的元素,不能重复选取。

weixin_43390031的博客 1090

代码随想录】【算法训练营】【第27天】 [39]组合总和 [40] 组合总和II [131]分割回文串

思路及算法思维,指路。题目来自。day 25,周一,库存没了,哭死~

day day up 621

代码随想录算法训练营day 26|第七章 回溯算法part03

首先要明确重复数组是怎样产生的,重复是由于之前用过的数字再次出现在数组的同一位置,于是就要保证同一位置也就是同一个for循环中不能出现相同的数字(每一次for循环实际上就是为path数组的当前位置在合适区间中选择一个数)(也就是文章中提到的树层去重),因为之前已经为candidates排过序了,所以相同的数字必然下标相邻,这就大大方便树层去重。接着就是文章所指的树枝去重,意为下一层递归的数字要从当前下标之后的剩下的数字中选择,因为考虑的是下一步的情况,所以叫树枝去重。本题开始涉及到一个问题了:去重。

mrmobi的博客 1012

二刷算法训练营Day27 (Day26 休息) | 回溯算法(3/6)

涉及到去重的回溯练习

记录学习每一天! 1286

代码训练营day 26 | 39. 组合总和 & 40.组合总和II & 131.分割回文串

代码训练营 day 26

weixin_48494936的博客 142

算法训练营笔记day26|39. 组合总和40. 组合总和 II131. 分割回文串

如果dfs里的index是0不是i,则结果会重复。

weixin_45662787的博客 121

二刷代码随想录——回溯day26

一个本硕双非的小菜鸡,备战24年秋招,计划二刷完卡子哥的刷题计划,加油!

qq_43264167的博客 435

leetcode 56合并区间

这道题你可以把它压缩成一个非常好记的思路:先排序,再逐个比较;能合并就扩右边界,不能合并就加入答案。具体来说,先用: 把所有区间按照左端点从小到大排序。排序之后,我们只需要拿“当前区间”和 里最后一个已经合并好的区间比较。如果: 说明前一个区间的右端点比当前区间左端点还小,二者没有重叠: 所以直接: 如果不满足: 那就说明: 两个区间重叠,例如: 此时左端点不用动,只需要把右端点扩大: 所以整道题可以记成: 这道题容易忘的函数/语法 含义:对整个 排序。对于:

qq_63176310的博客 226
上一篇: 代码随想录算法训练营day25| 216.组合总和III 、 17.电话号码的字母组合
m0_54840340
博客等级 码龄6年 1粉丝 · 7原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值