day22组合总和&组合总和III&分割回文串(切割问题:回溯三部曲)

本文详细解析了力扣平台的三个经典问题:组合问题39(回溯与重复节点)、组合总和II 40(去重技巧应用)和分割回文串131(切割与回文判断)。通过递归与剪枝策略,展示了如何解决这些问题并提供相关代码实现。
摘要由CSDN通过智能技术生成

        最后一个问题比较难,前两个利用模板可以解决。

1.力扣39(组合问题)

        经典的回溯问题,讲本题想象成上题这种树型结构,本题的特点是可以重复利用一个节点,也就是startIndex的的值就为i,但是我们必须要多加一个结束条件防止无限递归下去。回溯三部曲:

  • 递归函数参数:这里依然是定义两个全局变量,集合res存放结果集,集合path存放符合条件的结果;集合candidates, 和目标值target,此外我还定义了int型的sum变量来统计单一结果path里的总和,其实这个sum也可以不用,用target做相应的减法就可以了,最后如何target==0就说明找到符合的结果了,但为了代码逻辑清晰,我依然用了sum,还需要startIndex来控制for循环的起始位置。
    public void combination(int[] candidates, int target,int sum, int startIndex)
  • 递归终止条件:终止只有两种情况,sum大于target和sum等于target
        if(sum>target){
            return;
        }
        if(sum==target){
            res.add(new ArrayList(path));
            return;
        }
  • 单层递归逻辑:单层for循环依然是从startIndex开始,搜索candidates集合。
        for(int i=startIndex;i<candidates.length;i++){
            path.add(candidates[i]);
            combination(candidates,target,sum+candidates[i],i);
            path.remove(path.size()-1);
        }

整体代码:

    List<Integer> path = new ArrayList();
    List<List<Integer>> res = new ArrayList();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        combination(candidates,target,0,0);
        return res;
    }
    public void combination(int[] candidates, int target,int sum, int startIndex) {
        if(sum>target){
            return;
        }
        if(sum==target){
            res.add(new ArrayList(path));
            return;
        }
        for(int i=startIndex;i<candidates.length;i++){
            path.add(candidates[i]);
            combination(candidates,target,sum+candidates[i],i);
            path.remove(path.size()-1);
        }
    }

2.力扣40(组合总和II)

        本题最大的特点就是元素含有重复的,所以本题的关键就是进行去重的操作,在第一遍写的时候利用模板写出来的结果出现了重复,虽然值相同,但是确是索引不同,我们需要特殊的进行去重操作,此时我们需要理解树枝重复树层重复概念,也就是元素之前出现过的位置,同层出现就为树层重复,而树枝重复同理,在出现树层重复我们就跳过,就可以达到去重。回溯三部曲

  • 递归函数参数:此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。这个集合去重的重任就是used来完成的。
    public void combination(int[] candidates, int target,int sum,int startIndex)
  • 递归终止条件:终止条件为 sum > target 和 sum == target
        if(sum==target){
            res.add(new ArrayList(path));
            return;
        }
  • 单层搜索的逻辑:前面我们提到:要去重的是“同一树层上的使用过”,如果判断同一树层上元素(相同的元素)是否使用过了呢。如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。此时for循环里就应该做continue的操作。
        for(int i=startIndex;i<candidates.length&&sum+candidates[i]<=target;i++){
            if (i > 0 && candidates[i] == candidates[i - 1] && !used[i - 1]) {
                continue;
            }
            path.add(candidates[i]);
            used[i] = true;
            combination(candidates,target,sum+candidates[i],i+1);
            path.remove(path.size()-1);
            used[i] = false;
        }

 整体代码:

    List<Integer> path = new ArrayList();
    List<List<Integer>> res = new ArrayList();
    boolean[] used; 
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        used = new boolean[candidates.length];
        Arrays.fill(used,false);
        Arrays.sort(candidates);
        combination(candidates,target,0,0);
        return res;
    }
    public void combination(int[] candidates, int target,int sum,int startIndex) {
        if(sum==target){
            res.add(new ArrayList(path));
            return;
        }
        for(int i=startIndex;i<candidates.length&&sum+candidates[i]<=target;i++){
            if (i > 0 && candidates[i] == candidates[i - 1] && !used[i - 1]) {
                continue;
            }
            path.add(candidates[i]);
            used[i] = true;
            combination(candidates,target,sum+candidates[i],i+1);
            path.remove(path.size()-1);
            used[i] = false;
        }
    }

3.力扣131(分割回文串)

        本题属于回溯算法中的切割问题,需要好好理解,我们需要重新定义一个方法来判断是否为回文子串,并且在遍历的过程中进行剪枝操作,把不是回文子串的直接进行返回进行剪枝,而这个竖线我们利用竖线前的下标来解决。回溯三部曲

  • 递归函数参数:全局变量数组path存放切割后回文的子串,二维数组result存放结果集。 (这两个参数可以放到函数参数里)本题递归函数参数还需要startIndex,因为切割过的地方,不能重复切割,和组合问题也是保持一致的。
    public void part(String s,int startIndex) 
  • 递归函数终止条件:从树形结构的图中可以看出:切割线切到了字符串最后面,说明找到了一种切割方法,此时就是本层递归的终止终止条件;递归参数需要传入startIndex,表示下一轮递归遍历的起始位置,这个startIndex就是切割线。
        if(startIndex==s.length()){
            res.add(new ArrayList(path));
            return;
        }
  • 单层搜索的逻辑:在for (int i = startIndex; i < s.length(); i++)循环中,我们 定义了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。首先判断这个子串是不是回文,如果是回文,就加入在path中,path用来记录切割过的回文子串。
        for(int i=startIndex;i<s.length();i++){
            if(isisPalindrome(s,startIndex,i)){//是回文子串
                String str = s.substring(startIndex,i+1);
                path.add(str);
            }else{//不是回文子串
                continue;
            }
            part(s,i+1);
            path.remove(path.size()-1);
        }

整体代码:

    List<List<String>> res = new ArrayList();
    List<String> path = new ArrayList();
    public List<List<String>> partition(String s) {
        part(s,0);
        return res;
    }
    public void part(String s,int startIndex) {
        if(startIndex==s.length()){
            res.add(new ArrayList(path));
            return;
        }
        for(int i=startIndex;i<s.length();i++){
            if(isisPalindrome(s,startIndex,i)){//是回文子串
                String str = s.substring(startIndex,i+1);
                path.add(str);
            }else{//不是回文子串
                continue;
            }
            part(s,i+1);
            path.remove(path.size()-1);
        }
    }
    //判断是否是回文子串
    public boolean isisPalindrome(String s,int left,int right) {
        for(int i=left,j=right;i<=j;i++,j--){
            if(s.charAt(i)!=s.charAt(j)){
                return false;
            }
        }
        return true;
    }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值