代码随想录算法训练营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 + 1,i,根据之前做的得出
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
}
剪枝

对于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.单层遍历
对于单层需要注意的有两点
- 去除可能重复的情况分支
- 每个每个数字在每个组合中只能使用 一次
因此有
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)
遇到问题
- 对于字符串的分割不清晰
- 对于回文判断不明确
题解
切割思想如图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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;
}

1035




被折叠的 条评论
为什么被折叠?



