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

39. 组合总和 

1.candidates 中的 同一个 数字可以 无限制重复被选取

2.所有 不同组合

要求不同组合的话就需要设置参数startlex确保不会出现重复组合,即代码

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

candidates = [2,3,6,7], target = 7

为例,如果将循环条件设置为

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

那么会出现[2,2,3]和[3,2,2]这两种重复的情况。

第二个就是同一个数字可以无限选取,在设置递归条件时设置为

breaking(candidates,target,i);

这样即使这次使用了candidates[i],进入下一层递归时还可以使用candidates[i]。

主要还是以上两个条件不能弄混了。

——————————————————————————————————————————-

还有一个终止条件的处理,需要考虑到sum>target的情况

否则当sum>target时不会触发return,将会陷入死循环。

class Solution {
public:
    vector<vector<int>>result;
    vector<int>s1;
    int sum=0;
    void breaking(vector<int>& candidates, int target,int startlex)
    {
        if(sum==target)
        {
          result.push_back(s1);
          return;  
        }
        if(sum>target)
        return ;

        for(int i=startlex;i<candidates.size();i++)
        {
            s1.push_back(candidates[i]);
            sum+=candidates[i];
            breaking(candidates,target,i);
            
            sum-=candidates[i];
            s1.pop_back();
        }


    }


    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        breaking(candidates,target,0);
        return result;
    }
};

40.组合总和I

这题主要是去重的思想,借用代码随想录的思路

代码随想录

同一个树层不能出现重复元素,同一个树枝可以出现重复元素。

树层即为path中相同位置的不同元素导致的path的不同情况(多种path)

树枝即为组成一种path所依次插入元素的过程(单种path)

树层不重复是为了防止相同组合的出现

例子:

我们用1(1)代指第一个1,1(2)代指第二个1

树层不重复是为了防止[1(1),2,5],[1(2),2,5]的情况出现。

树枝不重复是为了防止同一种组合中出现相同的元素

还是以上图为例

防止[1(1),1(2),6]的情况出现。

现在问题是如何实现树层不重复,就是在1(1)进入path时,需要留下记号,防止1(2)进入path的相同位置。

如代码随想录的方法:

我在图中将used的变化用橘黄色标注上,可以看出在candidates[i] == candidates[i - 1]相同的情况下:

  • used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
  • used[i - 1] == false,说明同一树层candidates[i - 1]使用过

以[1(1),1(2),2]为例,当我们在进行树枝的构造时,使用过的元素,used对应的位置一定全是true,

for循环中backtracking下面的代码都没有用到,可以这样说

树枝的构造过程就是不断将used数组对应位置由false变为true的过程

树层的构造,就是这个循环已经完成了一次(i=0),进入下一层循环了(i=1)

此时1(1)为首元素的树层情况已经构建完成,1(1)对应的位置used已经又由true变为false了,所以考虑1(2)时,1(2)就不会再进入path了。

那么为什么used初始需要设置成false?是因为相同元素优先倾向同一树层已经使用过!

首先我们确定,只有前后两个元素相同才会触发used导致的跳跃事件

以[2,2,2],target=2为例,如果哦我们将used设置为true,现在的used就是[1,1,1]。

当i=0循环结束时。此时used已经变成[0,1,1],进入i=1的循环,由于满足continue的条件,i=1被跳过了,此时used仍为[0,1,1]

当i=2时,因为used[1]==1,不满足跳过条件,所以i=2循环会正常进行!!

本质原因就是连续出现continue情况时,used数组会因为跳过无法正确记录元素是否在同一树层使用过!(i=1)

所以我们将used初始值设置为false,相同元素优先倾向同一树层已经使用过!

131.分割回文串

此题是分割问题,进入循环的参数不再是下标,而是字符串分割点。

终止条件就是分割点大于等于size就会结束

至于为什么不是size-1时结束

首先就是size-1时还是可以继续分割的,(字符串最后一个字母单独分割出来),当这一分割结束时,

backtracking(s, i + 1);

进入下一层递归,此时分割点变为size,分割才算正式结束

还有就是循环中的代码执行

我们需要这样的效果:

是回文,正常递归和回溯

不是回文,直接跳过本次循环。

所以需要用if提前判断是否是回文,不是回文直接跳过。

可能有人有问题,你path.push_back(str);是在if{}内的,而 path.pop_back();不在if{}内,也就是说

插入数据是有条件的,排除数据是无条件的,万一还没进入数据就需要排出数据怎么办,所以

path.pop_back();也应该写在if内部

仔细分析代码可以明白,当跳过本次循环时,后面的pop是不会执行的!所以不会存在只出不入的情况。

for (int i = startIndex; i < s.size(); i++) {
            if (isPalindrome(s, startIndex, i)) {   // 是回文子串
                // 获取[startIndex,i]在s中的子串
                string str = s.substr(startIndex, i - startIndex + 1);
                path.push_back(str);
            } else {                                // 不是回文,跳过
                continue;
            }
            backtracking(s, i + 1); // 寻找i+1为起始位置的子串
            path.pop_back(); // 回溯过程,弹出本次已经添加的子串
        }
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值