代码随想录 day27 回溯算法 去重

代码随想录 day27

题39 组合总和

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

思考

1,弄清楚了回溯算法不难写出来大致思路,但自己第一遍写的时候并没有完全写对,问题在于startindex没有写对,造成了结果集中的重复,需要注意for循环的时候应该从startindex开始循环,而不是从0开始,另外本题中要求可以重复使用某元素,因此递归的时候仍然从当前元素开始而不是从下一个元素开始。

class Solution {
    LinkedList<Integer> path = new LinkedList<>();
    List<List<Integer>> result = new ArrayList<>();
    int sum = 0;
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backTracking(candidates, target, 0);
        return result;
    }

    public void backTracking(int[] candidates, int target, int startIndex) {
        if(sum > target){
            return;
        }
        if(sum == target){
            result.add(new ArrayList<>(path));
            return;
        }
        for(int i = startIndex; i < candidates.length; i++){
            sum += candidates[i];
            path.add(candidates[i]);
            backTracking(candidates, target, i);//从i 开始而不是从i+1开始
            sum -= candidates[i];
            path.removeLast();
        }
    }
}

题40 组合总和二

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。 解集不能包含重复的组合。
示例 1: 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
示例 2: 输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [ [1,2,2], [5] ]

思考:

1,自己尝试写了一遍,最后的结果集里有重复,对于如何去重没有搞清楚,不知道应该在哪里做去重的操作。

启发:

1,本题的难点在于给定数组中有重复元素,但要求结果集中不能有重复的组合。这就要求要先对原数组进行排序,并且在合适的时候要判断元素是否使用过,那么合适的时候是什么时候呢,应该是每一层的时候,比如[1,1,2,5,6] 和为8,则第一层时会得到[1,2,5], 那么第二层取到1的时候应该直接跳过,否则又会产生[1,2,5]。但还需要注意在每条路径上也就是遍历深度上不能去重,比如[1,1,6]这个结果是合法的。
2,那么应该如何实现层级上的去重呢,题解中提供了一种很巧妙的方式,引入一个布尔数组used来标记使用过的元素,使用过为true,未使用过为false。那么通过used又如何实现层级去重呢,当第i个元素和第i-1个元素相等并且used[i - 1] == false的时候代表层级遍历到了重复元素,因为此时可以理解为”第一次“(即使之前有相同的也已经收集完包含其的组合结果)遍历到该元素,此时如果和前面的元素相同就直接跳出本层循环,相当于此次不需要收集(因为相同的结果已经被收集过,再收集一次会重复)。
3, 那么如果used[i - 1] 为true呢?其实这种情况代表的是某条路径上的重复元素,这种情况是不需要去重的,比如[1,1,6]。

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    int[] used;//标记元素是否被使用过。
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        used = new int[candidates.length];
        Arrays.sort(candidates);//先将原数组进行排序
        backTracking(candidates, target, 0);
        return result;
    }

    public void backTracking(int[] candidates, int target, int startIndex){
        if(target < 0) return;
        if(target == 0){
            result.add(new ArrayList<>(path));
            return;
        }
        for(int i = startIndex; i < candidates.length ; i++){
            //判断层级重复元素
            if(i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == 0){
                continue;
            }
            path.add(candidates[i]);
            target -= candidates[i];
            used[i] = 1;
            backTracking(candidates, target, i + 1);
            //回溯过程
            target += candidates[i];
            path.removeLast();
            used[i] = 0;
        }
    }
}

题131 分割成回文子串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“a”,“b”] ]

启发

1,分割的问题也很适合采用回溯法。
2, 本题其实还是挺难的,看了题解以后其实还没有理解的特别透彻

class Solution {
    LinkedList<String> path = new LinkedList<>();
    List<List<String>> result = new ArrayList<>();
    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return result;
    }
    
    //startIndex可以代表切割线
    public 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(isPalindrome(s, startIndex, i)){
                String str = s.substring(startIndex, i + 1);
                path.add(str);
            } else {
                continue;
            }
            backTracking(s, i + 1);
            path.removeLast();
        }
    }

    //判断回文串
    public boolean isPalindrome(String s, int startIndex, int endIndex){
        for(int i = startIndex, j = endIndex; 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、付费专栏及课程。

余额充值