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

39. 组合总和

完成

思路:

本题中有一个特殊的条件:同一个数字可以无限制重复被选取。
因此在递归时,startIndex应该是i,而不是i+1

代码

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

再考虑如何剪枝,在调试测试用例[2,3,6,7]时发现,如果集合是递增的,前面的元素如果相加超出范围,那么也就无需考虑后面的元素了,必定也超范围。

因此可以先给集合排序。

class Solution {
    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> path = new ArrayList<>();
    public int sum = 0;
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
    	// 数组排序
        Arrays.sort(candidates);
        backtracking(candidates, target, 0);
        return res;
    }
    public void backtracking(int[] candidates, int target, int startIndex){
        if(sum == target){
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i < candidates.length; i++) {
        	// 直接跳出循环,第一个 > target的元素及其后面的元素都不遍历
            if(sum+candidates[i] > target) break;
            path.add(candidates[i]);
            sum+=candidates[i];
            backtracking(candidates, target, i);
            sum-=candidates[i];
            path.removeLast();
        }
    }
}

40.组合总和II

完成

思路:

题目要求解集不能包含重复的组合,但是候选数组中如果有重复元素,不加处理就会必定产生重复集合。

一开始的想法是先用set去重,再返回List,会超时。

因此只能在遍历时去重,组合问题可以抽象为树形结构,那么去重在这个树形结构上是有两个维度的,一个维度是同一树枝上去重,一个维度是同一树层上去重
本题应该是在同一树层上去重,同一树枝上无需去重。

代码

class Solution {
    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> path = new ArrayList<>();
    public int sum = 0;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtracking(candidates, target, 0);
        return res;
    }
    public void backtracking(int[] candidates, int target, int startIndex){
        if(sum == target){
            res.add(new ArrayList<>(path));
            return;
        }
        if(sum > target) return;
        for (int i = startIndex; i < candidates.length; i++) {
            // 同一树层去重,注意是i>startIndex,如果是i>0,树层和树枝都会去重
            if(i>startIndex && candidates[i] == candidates[i-1]) continue;
            
            path.add(candidates[i]);
            sum+=candidates[i];
            backtracking(candidates, target, i+1);
            sum-=candidates[i];
            path.removeLast();
        }
    }
}

131.分割回文串

完成

思路:

注意分割方法
在这里插入图片描述

代码

class Solution {
    //判断是否是回文串
    private boolean isPalindrome(String s, int startIndex, int end) {
        for (int i = startIndex, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }

    public List<List<String>> res = new ArrayList<>();
    public List<String> path = new ArrayList<>();

    public List<List<String>> partition(String s) {
        backtracking(s, 0);
        return res;
    }

     private void backtracking(String s, int startIndex) {
        //如果起始位置大于s的大小,说明找到了一组分割方案
        if (startIndex >= s.length()) {
            res.add(new ArrayList(path));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            //如果是回文子串,则记录
            if (isPalindrome(s, startIndex, i)) {
                String str = s.substring(startIndex, i + 1);
                path.addLast(str);
            } else {
                continue;
            }
            //起始位置后移,保证不重复
            backtracking(s, i + 1);
            path.removeLast();
        }
    }
}
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值