Java回溯算法中的组合问题 续

组合总和

注意本题的特色是没有数量要求可以无限重复但是有总和的限制所以间接的也是有个数的限制

           因为没有组合数量要求所以递归层数无限制

 回溯三部曲

  1. 递归函数参数

 首先全局变量两个 result和path

然后题目中所给的参数 集合candidates 目标值target

还可以定义int型的sum 

还需要startIndex来控制for循环的起始位置

   2.  递归终止条件

两种情况:1.  sum==target 收集结果

                   2.  sum>target 返回

   3.  单层搜索的逻辑

注意因为本题元素可以重复选取所以不需要i+1

剪枝优化

sum>target 时其实没有必要进入下一层递归

所以for循环可以进行优化 i<candidates.length&&sum+candidates[i]<=target

其他注意点

本题还需要先进行排序无重复元素),所以要Arrays.sort()

比如集合【2,2,3】【2,3,2】【3,2,2】其实是一个集合,所以递归遍历是i而不是startIndex

完整代码如下:

class Solution {
    List<List<Integer>> result=new ArrayList<>();
    LinkedList<Integer> path=new LinkedList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
       Arrays.sort(candidates);
        backtracking(candidates,target,0,0);
        return result;
    }
    private void backtracking(int[] candidates,int target,int sum,int startIndex){
        if(sum>target) return;
        if(sum==target){
            result.add(new ArrayList<>(path));
            return;
        }
        for(int i=startIndex;i<candidates.length&&sum+candidates[i]<=target;i++){
            sum+=candidates[i];
            path.add(candidates[i]);
            backtracking(candidates,target,sum,i);
            sum-=candidates[i];
            path.removeLast();
        }
    }
}

组合总和||

本题与上题的区别在于

  1. 本题candidates中的每个数字在每个组合中只能使用一次
  2. 本题数组candidates的元素是有重复的而上题元素无重复

本题难点:集合有重复元素但不能有重复组合 即使用过的的元素不能重复选取即去重

                  使用过在树形结构上有两个维度:1.同一树枝上使用过 2.同一树层上使用过

本题需要去重的是同一树层上的重复元素同一树枝上的都是一个组合里的元素不需要去重注意树层去重需要对数组排序

回溯三部曲

  1. 递归函数参数

         两个全局变量 result和path

         在上题基础上需要多定义一个bool型数组记录同一树枝上元素是否使用过

   2.   递归终止条件

          与上题相同

   3.   单层搜索的逻辑

        排序后 candidatesi】=-candidatesi-1并且usedi-1】==false说明同一树层使用过

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

其实根本不需要used数组去重,直接用startIndex来去重也可以,

即当i>startIndex&&candidates[i]==candidates[i-1]对同一树层使用过的元素进行跳过

完整代码如下:

class Solution {
    List<List<Integer>> result =new ArrayList<>();
    LinkedList<Integer> path =new LinkedList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtracking(candidates,target,0,0);
        return result;
    }
    private void backtracking(int[] candidates,int target,int sum,int startIndex){
        if(sum==target){
            result.add(new ArrayList<>(path));
            return;
        }
        for(int i=startIndex;i<candidates.length&&sum+candidates[i]<=target;i++){
            if(i>startIndex&&candidates[i]==candidates[i-1]){
                continue;
            }
            sum+=candidates[i];
            path.add(candidates[i]);
            backtracking(candidates,target,sum,i+1);
            sum-=candidates[i];
            path.removeLast();

        }
    }
}

分割回文串

回文串正着读和反着读都一样的字符串

本题涉及两个关键问题

  1. 切割问题有不同的切割方式
  2. 判断回文

其实切割问题类似组合问题

回溯三部曲

  1. 递归函数参数

两个全局变量 result和path

还需要定义int型startIndex

   2.  递归函数终止条件

切割线切到字符串最后面则终止条件

什么是切割线呢

传入的startIndex就是切割线

  3.  单层搜索的逻辑

首先判断这个字串是不是回文若是则加入path

注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1

这里就需要定义一个方法来判断是否是回文字串

使用双指针法判断

还需要注意 截取字符串substring()是一个左闭右开的区间

完整代码如下:

class Solution {
    List<List<String>> result=new ArrayList<>();
    LinkedList<String> path=new LinkedList<>(); 
    public List<List<String>> partition(String s) {
        backtracking(s,0);
        return result;
    }
    private void backtracking(String s,int startIndex){
        if(startIndex>=s.length()){
            result.add(new ArrayList<>(path));
            return;
        }
        for(int i=startIndex;i<s.length();i++){
            if(decidehuiwen(s,startIndex,i)){
                String str=s.substring(startIndex,i+1);
                path.add(str);
            }else{
                continue;
            }
            backtracking(s,i+1);
            path.removeLast();
        }
    }
    public boolean decidehuiwen(String s,int startIndex,int m){
        for(int i=startIndex,j=m;i<j;i++,j--){
            if(s.charAt(i)!=s.charAt(j)){
                return false;
            }
        }
        return true;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值