组合总和问题---39. Combination Sum && 40. Combination Sum II && 216. Combination Sum III

组合总和问题—39. Combination Sum && 40. Combination Sum II && 216. Combination Sum III

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

1. 题目
39. Combination Sum

Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
The same repeated number may be chosen from candidates unlimited number of times.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [2,3,6,7], target = 7,
A solution set is:
[
[7],
[2,2,3]
]
Example 2:

Input: candidates = [2,3,5], target = 8,
A solution set is:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

2. 题目分析
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。这其实跟硬币找零问题差不多,就是给定一些数值,通过这些数值(元素数量无限)组成固定的数字。

3. 解题思路
题目给了我们一个candidates array 和一个 target, 让我们从array 里找到所有的组合,它的和是等于target的。任何组合只要是它的和等于target的话,都需要找到,但是不需要重复的。这道题中是可以重复利用一个数字的,那么我们就需要每次都代入同一个数字,直到它之和达到target 或者它超过了target, 然后在倒退回去一个数字,继续找下一个数字,这种情况肯定是要用递归了。这里是backtracking,每次倒退回一个数字,需要继续递归下去,在倒退,一直重复直到搜索了所有的可能性。

4. 代码实现(java)

package com.algorithm.leetcode.backtracking;

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

/**
 * Created by 凌 on 2019/1/30.
 * 注释:39. Combination Sum
 */
public class CombinationSum {
    /**
     * 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
     candidates 中的数字可以无限制重复被选取。
     * @param candidates
     * @param target
     * @return
     */
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        if (candidates == null || target < 0){
            return new ArrayList<>();
        }
        Arrays.sort(candidates);
        List<List<Integer>> combineList = new ArrayList<>();
        List<Integer> combineCeil = new ArrayList<>();

        int start = 0;
        dfs(combineList,combineCeil,candidates,target,start);
        return combineList;
    }

    public void dfs(List<List<Integer>> combineList,List<Integer> combineCeil,int[] candidates, int target,int start){
        if (target < 0){
            return;
        }else if (target == 0){
            combineList.add(new ArrayList<>(combineCeil));
            return;
        }else{
            for (int i = start; i < candidates.length; i++) {
                combineCeil.add(candidates[i]);
                dfs(combineList,combineCeil,candidates,target - candidates[i],i);
                combineCeil.remove(combineCeil.size()-1);
            }
        }

    }
}

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

1. 题目
40. Combination Sum II

Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
Each number in candidates may only be used once in the combination.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
Example 2:
Input: candidates = [2,5,2,1,2], target = 5,
A solution set is:
[
[1,2,2],
[5]
]

2. 题目分析
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字只能被选取一次。
不同点: 不能重复使用同一个位置上的元素,但是不同位置上可能有相同值的元素,所以–>需要考虑重复答案的问题

3. 解题思路
39. Combination Sum && 40. Combination Sum II && 216. Combination Sum III 等回溯问题,其实都是一个套路,都是需要另写一个递归函数,这里我们新加入三个变量,start记录当前的递归到的下标,combineCeil为一个中间解,combineList保存所有已经得到的解,每次调用新的递归函数时,此时:

  • target如果等于0,说明已经找到和为target的组合了;
  • target如果小于0,说明加入组合的上一个数太大了,另外由于数组时已经进行升序排序,所以往后加入的数字也一定会大于target,则直接结束递归;
  • 如果大于0,说明,往后递归还有希望找到等于target的组合。并且这里跟前面一题不一样的地方是,这道题定数组中的数字不能重复使用,且可能存在相同元素。只需要在之前的基础上修改两个地方即可,首先在递归的for循环里加上if (i > start && candidates[i] == candidates[i - 1]) continue; 这样可以防止combineList中出现重复项,然后将递归调用dfs里面的参数换成i+1,这样就不会重复使用数组中的数字了。

4. 代码实现(java)

package com.algorithm.leetcode.backtracking;

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

/**
 * Created by 凌 on 2019/2/3.
 * 注释:40. Combination Sum II
 */
public class CombinationSum2 {
    public static void main(String[] args) {
        CombinationSum2 combinationSum2=new CombinationSum2();
        int[] candidates = {10,1,2,7,6,1,5};
        int target = 8;
        List<List<Integer>> result = combinationSum2.combinationSum2(candidates,target);
        System.out.println(result.toArray());
    }

    /**
     * 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
     candidates 中的数字只能被选取一次。
     不同点: 不能使用同一个位置上的元素,但是不同位置上可能有相同值的元素,所以-->需要考虑重复答案的问题
     * @param candidates
     * @param target
     * @return
     */
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        if (candidates == null || target < 0){
            return new ArrayList<>();
        }
        Arrays.sort(candidates);
        List<List<Integer>> combineList = new ArrayList<>();
        List<Integer> combineCeil = new ArrayList<>();

        int start = 0;
        dfs(combineList,combineCeil,candidates,target,start);
        return combineList;
    }

    public void dfs(List<List<Integer>> combineList,List<Integer> combineCeil,int[] candidates, int target,int start){
        if (target < 0){
            return;
        }else if (target == 0){
            combineList.add(new ArrayList<>(combineCeil));
        }else{
            for (int i = start; i < candidates.length; i++) {
                if (i > start && candidates[i] == candidates[i-1]){
                    continue;
                }
                combineCeil.add(candidates[i]);
                dfs(combineList,combineCeil,candidates,target - candidates[i],i+1);
                combineCeil.remove(combineCeil.size()-1);
            }
        }
    }
}

1. 题目
216. Combination Sum III

Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.

Note:

All numbers will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: k = 3, n = 7
Output: [[1,2,4]]
Example 2:
Input: k = 3, n = 9
Output: [[1,2,6], [1,3,5], [2,3,4]]

2. 题目分析
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
解集不能包含重复的组合。

3. 解题思路
这题其实就是前两题的结合体,给定的数字变成1-9,然后要求找到的组合不存在相同数字。

4. 代码实现(java)

package com.algorithm.leetcode.backtracking;

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

/**
 * Created by 凌 on 2019/2/3.
 * 注释:216. Combination Sum III
 */
public class CombinationSum3 {
    public static void main(String[] args) {
        CombinationSum3 combinationSum3=new CombinationSum3();
        int k = 3;
        int target = 7;
        List<List<Integer>> result = combinationSum3.combinationSum3(k,target);
        System.out.println(result.toArray());
    }

    /**
     * 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
     解集不能包含重复的组合
     * @param k
     * @param target
     * @return
     */
    public List<List<Integer>> combinationSum3(int k, int target) {
        if (target > 45 || target < 0){
            return new ArrayList<>();
        }

        List<List<Integer>> combineList = new ArrayList<>();
        List<Integer> combineCeil = new ArrayList<>();

        int start = 1;
        dfs(combineList,combineCeil,target,k,start);
        return combineList;
    }

    public void dfs(List<List<Integer>> combineList, List<Integer> combineCeil, int target,int k, int start){
        if (target < 0){
            return;
        }else if (combineCeil.size() == k && target == 0){
            combineList.add(new ArrayList<>(combineCeil));
        }else{
            for (int i = start; i <= 9; i++) {
                combineCeil.add(i);
                dfs(combineList,combineCeil,target - i, k,i+1);
                combineCeil.remove(combineCeil.size()-1);
            }
        }
    }
}

你可能感兴趣的:(leetcode刷题,leetcode算法刷题)