leetcode-40-组合总和回溯算法记录

前言

        很少写关于leetcode相关算法的解法,这篇则是由于回溯的剪枝策略过于优秀,我在一段时间都没想到,于是感觉可以记录下来,加深自己印象的同时分享一下。

正文

        题目就是leetcode的第40道题,组合总和2,描述如下:

给定一个数组 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]
]

解题思路

        首先我们在进行题目解法思考时,基本上都可以考虑到这是一个回溯算法,应该使用dfs解法;其次又要考虑到去重,因此一个“排序”数组对于我们是很有用的,在去重时一个最简单的方案就是对于生成的数组进行内部排序对比去重,当然这样又low,效率又低,因此我们需要有一个灵活的剪枝操作。如下图所示:

在这里插入图片描述

       如上图所示,以下几个步骤来解决:

  1. 我们在对原始数组 [2,5,2,1,2]进行排序后得到[1,2,2,2,5],然后进行dfs的回溯遍历;
  2. 接着出现了第一个剪枝的操作,因为我们是排序的数组,所以如果后续的总和比我当前的index数字还要大的话,就不需要进行下一个部分了。
  3. 然后我们还要进行相同数字所造成答案中重复数组的剪枝去重操作,这块是我认为最难的一个点,我们如何能保证以下两点是关键:
                  1
                 / \
                2   2  这种A情况不会发生 但是却允许了不同层级之间的重复即:
               /     \
              5       5

                  1
                 /
                2      这种B情况确是允许的
               /
              2  
                

       那么我们如何能避免A情况,candidates[i] == candidates[i - 1]时跳过,但是这样也会是B情况被跳过,A和B的区别在哪,就是在于A和B的重复元素的位置不一样,B在于重复元素不在同一层,因此我们再加一个条件,当进行dfs for循环时,i > index层数不在一层时进行跳过,代码如下:

public class Solution {
    public static void main(String[] args) {
        int[] arr1 = {10, 1, 2, 7, 6, 1, 5};
        int target1 = 8;
        List<List<Integer>> result1 = combinationSum2(arr1, target1);

        int[] arr2 = {2, 5, 2, 1, 2};
        int target2 = 5;
        List<List<Integer>> result2 = combinationSum2(arr2, target2);
        System.out.println();
    }

    public static List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);

        List<List<Integer>> result = new ArrayList<>();
        List<Integer> nowList = new ArrayList<>();

        int index = 0;
        dfs(result, nowList, index, target, candidates);
        //dfs时对重复数据进行剪枝操作。
        return result;
    }

    private static void dfs(List<List<Integer>> result, List<Integer> nowList, int index, int target, int[] candidates) {
        if (target == 0) {
            result.add(new ArrayList<>(nowList));
            return;
        }

        for (int i = index; i < candidates.length; i++) {
            if (i > index && candidates[i] == candidates[i - 1]) {
                continue;
            }
            if (target < candidates[i]) {
                break;
            }
            target = target - candidates[i];
            nowList.add(candidates[i]);
            dfs(result, nowList, i + 1, target, candidates);
            nowList.remove(nowList.size() - 1);
            target = target + candidates[i];
        }
    }
}

结语

        在一些排列组合等dfs回溯算法时,进行剪枝操作都会比较清晰、条件比较容易得出,这个题目的剪枝相对比较巧妙,因此记录下来,在考虑剪枝防止剪到正常枝叶时,需要对几种情况综合对比考虑即可。

参考:https://leetcode-cn.com/problems/combination-sum-ii/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-3/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值