leetcode刷题日记-【】

79 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情

题目描述

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

 

示例 1:

输入:candidates = [2,3,6,7], target = 7 输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。 示例 2:

输入: candidates = [2,3,5], target = 8 输出: [[2,2,2,2],[2,3,3],[3,5]] 示例 3:

输入: candidates = [2], target = 1 输出: []  

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都 互不相同
  • 1 <= target <= 500

题目元素

给定一个无重复的数组和一个整数target,从这个数组中组合出所有的组合,使这些组合中的元素之和等于target。数组中的元素可以重复,但是多个数组中的所有元素不能相同。

解题思路

仍旧是从数组中找到元素等于目标值的题,这道题与之前的题的区别是同一个元素可以重复使用且数组元素个数没有限制。所以这道题用双指针的话难度较大,因为左指针和右指针的移动条件变得比较复杂。

可以考虑用回溯进行解答。同一个元素可以做两种选择,继续选择当前元素;选择下一个元素。所以所有可以组合的元素数组分为两部分dfs(选择当前元素)+dfs(选择下一个元素);

回溯的三要素:

  • path:已选择的路径
  • 可选择的路径:还未选择的元素或者当前元素
  • 终止条件:当前target=0或者给定数组中的元素已经为空

数组还涉及到去重的问题,这个因为给定的数组本身不是重复的,所以不需要在做元素选择的时候跳过重复的元素,只需要将数组转成线性数组,然后从前往后遍历元素,获得满足条件的组合,得到的所有满足条件的数组一定是不重复的。

将数组转成升序数组还有个好处就是可以及时对选择剪枝,当前面的元素已经大于target时,后面的元素也没有必要再进行选择了。

代码实现

public static List<List<Integer>> combinationSum(int[] candidates, int target) {
    List<List<Integer>> res = new ArrayList<>();
    // 创建路径
    ArrayDeque<Integer> path = new ArrayDeque<>();
    Arrays.sort(candidates);
    // 回溯
    backTrace(candidates,0,path,target,res);
    return res;
}

/**
 * 回溯算法
 *
 * @param candidates 所有元素
 * @param index 当前已选择的下标,可选路径就时下标>=index 下标从0开始
 * @param path 当前已选择的所有路径
 * @param target 还剩的目标值
 * @param res 所有满足条件的路径
 */
private static void backTrace(int[] candidates, int index, ArrayDeque<Integer> path, int target,List<List<Integer>> res) {
    if (index >= candidates.length){
        return;
    }
    if (target == 0) {
        res.add(new ArrayList<>(path));
        return;
    }
    // 循环当前可选择的路径
    // 剪枝,当前元素如果已经大于target直接返回
    if (candidates[index] > target) {
        return;
    }
    // 选择下一个元素
    backTrace(candidates,index+1,path,target,res);
    // 选择当前元素
    if (target-candidates[index] >= 0) {
        // 做选择
        path.addLast(candidates[index]);
        // 选择当前元素
        backTrace(candidates,index,path,target-candidates[index],res);
        // 回退
        path.pollLast();
    }
}