LeetCode--39.组合总和、40组合总和II

LeetCode--39.组合总和、40组合总和II做题笔记

  • 39.组合总和
    • 题目描述
    • 解题思路
    • 代码(java)
  • 40.组合总和II
    • 题目描述
    • 解题思路
    • 代码(java)

39.组合总和

题目描述

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。说明:所有数字(包括 target)都是正整数。解集不能包含重复的组合。
示例 1:输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
提示:1 <= candidates.length <= 301 <= candidates[i] <= 200candidate 中的每个元素都是独一无二的。1 <= target <= 500

解题思路

理清题目。算法思想:回溯。算法结构:递归。
1.先对数组做一个升序排序,本意是求相加等于target,换一种思路就是减法的思想,用target一个一个减去数组里的元素,当等于零时就是一组答案,这里选择从大的数开始减,即从数组最后一个元素开始减。
2.既然用到递归,那么就得有递归结束判断条件,否则就会进入死循环。
用target挨个减的过程中,会有大于零、等于零、小于零的结果。
 a.如果等于零,则找到一组答案,然后return即可结束。(结束条件1)
 b.如果小于零,因为是排好序的数组,再往下找也不会有答案,直接return结束。(结束条件2)
 c.如果大于零,那么继续递归。

代码(java)

class Solution {
    List<List<Integer>> ans = new ArrayList<>();//全局
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<Integer> list = new ArrayList<>();//用于存储每一个符合的集合
        Arrays.sort(candidates);
        dfs(candidates.length - 1, target, candidates, list);
        return ans;
    }
    void dfs(int star, int target, int[] nums, List<Integer> list) {
        if (target < 0)
            return;
        else if (target == 0) {
            ans.add(new ArrayList<>(list));
            return;
        } else {
            for (int i = star; i >= 0; i--) {
                if (target < nums[i])
                    continue;
                list.add(nums[i]);
                dfs(i, target - nums[i], nums, list);//每一个元素可重复使用,继续从该元素开始
                list.remove(list.size() - 1);//target值小于零return之后,回到上一节点并去掉该节点
            }
        }
    }
}

40.组合总和II

题目描述

给定一个数组 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.每一个数只能使用一次,不能重复使用;2.数组中可能包含重复数据。
单看第一点,既然数组元素不能重复使用,那么在递归时就让它从下一个元素开始,代码修改:

//原来递归从i开始
 dfs(i, target - nums[i], nums, list)
//参数修改为下一个开始遍历,i-1
 dfs(i-1, target - nums[i], nums, list)

然后看第二点,数组中可能包含相同元素,当前代码会出现的bug:
bug1.例如示例 1:输入: candidates = [10,1,2,7,6,1,5], target = 8,
排好序后candidates = [1,1,2,5,6,7,10]
解集candidates[0],candidates[2],candidates[3]即[1,2,5]
解集candidates[1],candidates[2],candidates[3]即[1,2,5]导致重复。
首先想到的解决方法是加一个判定条件,让它跳过该层循环进入下一层循环

 if (nums[i] == nums[i + 1])                    
     continue;

然而此时第二个bug出现,
bug2.会错失统计解集candidates[0],candidates[1],candidates[4],[1,1,6]
盗用一个非常完美的解答:同层去重
LeetCode--39.组合总和、40组合总和II_第1张图片
于是代码修改为:

if (i < star && nums[i] == nums[i + 1])
    continue;
    //i是当前考察的元素下标,start是本层最开始的那个元素的下标

代码(java)

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<Integer> list = new ArrayList<>();
        Arrays.sort(candidates);
        dfs(candidates.length - 1, target, candidates, list);
        return ans;
    }
    void dfs(int star, int target, int[] nums, List<Integer> list) {
        if (target < 0)
            return;
        else if (target == 0) {
            ans.add(new ArrayList<>(list));
        } else {
            for (int i = star; i >= 0; i--) {
                if (target < nums[i])
                    continue;
                if (i < star && nums[i] == nums[i + 1]) //i是当前考察的元素下标,start是本层最开始的那个元素的下标
                    continue;
                list.add(nums[i]);
                dfs(i-1, target - nums[i], nums, list);//参数修改为下一个开始遍历,i-1
                list.remove(list.size() - 1);
            }
        }
    }
}

你可能感兴趣的:(笔记)