LC39-组合总和----LC40-组合总和 II

文章目录

39. 组合总和

难度中等1141

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

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

思路分析:根据示例 1:输入: candidates = [2, 3, 6, 7],target = 7。

候选数组里有 2,如果找到了组合总和为 7 - 2 = 5 的所有组合,再在之前加上 2 ,就是 7 的所有组合;
同理考虑 3,如果找到了组合总和为 7 - 3 = 4 的所有组合,再在之前加上 3 ,就是 7 的所有组合,依次这样找下去。

详细思路,图解详情

回溯,减支

package com.nie.OneJanLC;/*
 *
 *@auth  wenzhao
 *@date 2021/1/26  11:03
 */

import java.util.ArrayList;
import java.util.List;

public class LEE039 {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        getRes(res, new ArrayList<>(), candidates, target, 0);
        return res;

    }

    /**
     * @param res        结果集合
     * @param list       从根结点到叶子结点的路径,是一个栈
     * @param candidates 候选数组
     * @param target     每减去一个元素,目标值变小
     * @param start      搜索起点
     */
    private void getRes(List<List<Integer>> res, List<Integer> list, int[] candidates, int target, int start) {
        if (target == 0) {
            res.add(new ArrayList<>(list));
            return;
        }
        // 重点理解这里从 start  开始搜索的语意
        for (int i = start; i < candidates.length; i++) {
            // 重点理解这里剪枝,前提是候选数组已经有序, 数组中剩余的数大于要要求和的
            if (target < candidates[i]) {
                continue;
            }
            list.add(candidates[i]);
            // 注意:由于每一个元素可以重复使用,下一轮搜索的起点依然是 i,这里非常容易弄错
            getRes(res, list, candidates, target - candidates[i], i);
            // 状态重置
            list.remove(list.size() - 1);
        }
   }
}

40. 组合总和 II

难度中等478

给定一个数组 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]
]
package com.nie.OneJanLC;/*
 *
 *@auth  wenzhao
 *@date 2021/1/26  11:03
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LEE040 {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates);
        getRes(candidates, new ArrayList<>(), res, target, 0);
        return res;
    }

    private void getRes(int[] candidates, ArrayList<Integer> list, List<List<Integer>> res, int target, int start) {
        if (target == 0) {
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = start; i < candidates.length; i++) {
            // 大剪枝:减去 candidates[i] 小于 0,减去后面的 candidates[i + 1]、candidates[i + 2] 肯定也小于 0,因此用 break
            if (target < candidates[i]) {
                continue;
            }
            // 小剪枝:同一层相同数值的结点,从第 2 个开始,候选数更少,结果一定发生重复,因此跳过,用 continue
            if (i > start && candidates[i] == candidates[i - 1]) {
                continue;
            }
            list.add(candidates[i]);
            // 因为元素不可以重复使用,这里递归传递下去的是 i + 1 而不是 i
            getRes(candidates, list, res, target - candidates[i], i + 1);
            list.remove(list.size() - 1);
        }
    }
}

:解释语句: if cur > begin and candidates[cur-1] == candidates[cur] 是如何避免重复的。

摘选自

这个避免重复当思想是在是太重要了。
这个方法最重要的作用是,可以让同一层级,不出现相同的元素。即
                  1
                 / \
                2   2  这种情况不会发生 但是却允许了不同层级之间的重复即:
               /     \
              5       5
                例2
                  1
                 /
                2      这种情况确是允许的
               /
              2  
                
为何会有这种神奇的效果呢?
首先 cur-1 == cur 是用于判定当前元素是否和之前元素相同的语句。这个语句就能砍掉例1。
可是问题来了,如果把所有当前与之前一个元素相同的都砍掉,那么例二的情况也会消失。 
因为当第二个2出现的时候,他就和前一个2相同了。
                
那么如何保留例2呢?
那么就用cur > begin 来避免这种情况,你发现例1中的两个2是处在同一个层级上的,
例2的两个2是处在不同层级上的。
在一个for循环中,所有被遍历到的数都是属于一个层级的。我们要让一个层级中,
必须出现且只出现一个2,那么就放过第一个出现重复的2,但不放过后面出现的2。
第一个出现的2的特点就是 cur == begin. 第二个出现的2 特点是cur > begin.
上一篇:【技术分享】angr:基于python的二进制分析框架


下一篇:【深度优先搜索DFS】Lintcode 135. 数字组合