补day29-回溯3

本文介绍了如何使用递归方法解决组合总和问题(允许重复和不重复元素),并通过去重处理解决组合总和II问题,以及如何通过双指针法实现分割回文串的算法。主要涉及递归函数定义、终止条件和单层搜索逻辑的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  •  39. 组合总和
  •  40.组合总和II
  •  131.分割回文串

第一题:组合总和

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

0、思路:数组内的元素可以重复选择,树的深度不受限,直到数组的和为target,然后返回

代码随想录的第二层思想选了a,第二个分支就不考虑a

1、递归三部曲

(1)递归函数参数以及定义变量

定义一个二维数组reslut存放结果集,数组path存放符合条件的结果。递归函数的传进的参数是数组,目标值,求和变量,以及startIndex

vector<vector<int>> result;

vector<int> path;

void backtracking (vector<int> & candidates, int target,int sum,int startIndex)

(2)递归终止条件

从树形图可以看出来,停止的情况一共三种:sum==target;sum>target

if(sum>target){

return;

}

if(sum==target){

result.push_back(path);

return;

}

(2)单层搜索的逻辑

还是从startIndex开始,搜索candidates集合,重点在于可以重复选取的情况

for(int i=startIndex ; i<candidates.size(); i++){

sum+=candidates[i];

path.push_back(candidates[i];

backtracking(candidates,target,sum,i));    //可以重复取元素,所以不用i+1

sum-=candidates[i];   //回溯

path.pop_back();    //回溯

}

第二题:组合总和 2

本题相较于上题需要去重,给的数组里可能存在重复元素,这么看的话这种题好像可以求最优解。

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

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

说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。

0、关于去重的问题,假如数组为[1,1,2],有两个1,深度方向上也就是同一树枝方向可以取相同的;但是横向方向也就是同一树层方向不可以取相同元素,因为很可能重复

相比上题需要多一个判断过程,记录同一树枝上的元素是否是使用过,用下面的used来记录

(1)递归函数参数

vector<vector<int>> result;

vector<int> path;

void backtracking(vector<int>& candidates, int target,int sum ,int startIndex,vector<bool> used)

{}

(2)递归终止条件

跟上题一样,要么sum>target;要么sum==target

if(sum>target){   

return;}

if(sum==target){

result.push_back(path);

return;

}

(3)单层搜索逻辑

本题重点在于树层去重和树枝去重两方面,树层去重上相邻元素一个元素一个元素的取。

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]使用过

//针对上面的注释,按理来说,只要元素a使用过,它的位置对应的used[i]就会变成true,但是如果发生了回溯,也就是从下一树层回溯到上一树层,true又会变成false,所以方便同树层辨别

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);

used[i]=false;

sum-=candidates[i];

path.pop_back();

}

第三题:分割回文串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

实质和前面的一样,不过求组合变成了将原来的元素切成不同的长度,比如第一种切法结果是{a,a,b}  第二种切法结果是{aa,b} 第三种只有{aab}

1、递归三部曲

(1)定义全局变量和递归函数参数

存放结果的result,存放切割的子串的path,避免重复的startIndex

vector<vector<string>> result;
vector<string> path; // 放已经回文的子串
void backtracking (const string& s, int startIndex) {

(2)定义递归终止条件

分割线其实就是startIndex,也就是数组的起始位置,当起始位置大于s的长度时终止

void backtracking(const string& s,int startIndex){

if(startIndex>=s.size()){

result.push_back(path);

return;}

}

(3)单层搜索的逻辑

定义起始位置startIndex,那么[startIndex,i]就是要截取的子串,每一次递归都要判断这一段子串是否是回文串,是的话将其放入path,如果不是直接跳过

1.用双指针法判断是否是回文串

bool isPalindrome(const string& s, int start, int end) {
     for (int i = start, j = end; i < j; i++, j--) {
         if (s[i] != s[j]) {
             return false;
         }
     }
     return true;
 }

2.找到是回文串的字符区间,将他的子串全部求出来,然后放进path中

if (isPalindrome(s, startIndex, i)) {   // 是回文子串
                // 获取[startIndex,i]在s中的子串
                string str = s.substr(startIndex, i - startIndex + 1);
                path.push_back(str);
            } else {                                // 不是回文,跳过
                continue;
            }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值