LeetCode 39. Combination Sum
2020-05-24 18:03:47 # leetcode # core problems

Problem

LeetCode 39. Combination Sum

1. 题目简述

给出一个无重复的正整数数组A,给出一个target,找出和为target的所有不重复的可能组合(每个数字使用次数不限)。例如:

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

2. 算法思路

这道题是combination sum系列的第一题,看似有点像背包问题,实际上又不是。这道题问的是所有组合,某些DP问题也是求所有的组合之类的,但是本题要求不能有相同元素组成的组合,且找不到所谓的最优子问题结构,因此所以并不是DP问题(等复习到背包问题再回过来看)。

Brute Force(行不通)

首先,最容易想到的就是能不能用暴力破解法呢?答案肯定是不行的,由于每个数字取的次数不限,所以直接暴力破解不可取,需要优化。

回溯

那么为什么会想到回溯呢?没有为什么,因为回溯能做出来这种需要遍历所有可能的问题。就这么简单。

对于一个数组A和某个target值,我们希望找出target所有的组合可能,假设F(int[] A, int start, int target)为从数组A中以start为起点找出target的所有可能组合,思路如下。

  1. 对A从小到大进行排序,取A[0]作为第一个数,那么我们当前的目标是F(A, 0, target - A[0]);将其结果和A[0]放在一起作为第一种解。
  2. 然后取A[1]作为第一个数,然后我们目标是F(A, 1, target - A[1])。不从0开始是因为如果取了0,就会和第一步中的某种solution重复,故不取。
  3. 终止条件为target == 0,最终结果添加一条;target < 0,回溯上去继续搞。

3. 解法

  1. 暴力解法(Pass)
  2. 回溯
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

class Solution {
List<List<Integer>> res;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
res = new ArrayList();
LinkedList<Integer> nums = new LinkedList(); //删除last回溯的时候方便
Arrays.sort(candidates);

backtracking(candidates, target, 0, 0, nums);

return res;
}

private void backtracking(int[] candidates, int target, int sum, int startIndex, LinkedList<Integer> nums) {
for (int i = startIndex; i < candidates.length; i++){
sum += candidates[i];
nums.add(candidates[i]);

if (sum < target) {
backtracking(candidates, target, sum, i, nums);
sum -= candidates[i];
nums.removeLast();
continue;
} else if (sum == target) {
res.add(new ArrayList(nums));
}

nums.removeLast();
return;
}
}
}