网址
题目
给定一个数组和一个值,找出所有满足相加为这个值的数的集合。
解法1
- 第一眼看到这题就知道应该用回溯做,但具体的怎么写代码又有点懵,看了看讨论区的代码,其实一般的架构就是一个大的 for 循环,然后先 add,接着利用递归进行向前遍历,然后再 remove
import java.util.Arrays;
class Solution {
public List<List<Integer>> combinationSum(int[] nums, int target) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, target, 0);
return list;
}
private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){
if(remain < 0) return;
else if(remain == 0) list.add(new ArrayList<>(tempList));
else{
for(int i = start; i < nums.length; i++){
tempList.add(nums[i]);
backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements
tempList.remove(tempList.size() - 1);
}
}
}
}
解法2 动态规划
- 来源于博客
- 但是最后需要去重复
public List<List<Integer>> combinationSum(int[] nums, int target) {
List<List<List<Integer>>> ans = new ArrayList<>(); //opt 数组
Arrays.sort(nums);// 将数组有序,这样可以提现结束循环
for (int sum = 0; sum <= target; sum++) { // 从 0 到 target 求出每一个 opt
List<List<Integer>> ans_sum = new ArrayList<List<Integer>>();
for (int i = 0; i < nums.length; i++) { //遍历 nums
if (nums[i] == sum) {
List<Integer> temp = new ArrayList<Integer>();
temp.add(nums[i]);
ans_sum.add(temp);
} else if (nums[i] < sum) {
List<List<Integer>> ans_sub = ans.get(sum - nums[i]);
//每一个加上 nums[i]
for (int j = 0; j < ans_sub.size(); j++) {
List<Integer> temp = new ArrayList<Integer>(ans_sub.get(j));
temp.add(nums[i]);
ans_sum.add(temp);
}
} else {
break;
}
}
ans.add(sum, ans_sum);
}
return removeDuplicate(ans.get(target));
}
private List<List<Integer>> removeDuplicate(List<List<Integer>> list) {
Map<String, String> ans = new HashMap<String, String>();
for (int i = 0; i < list.size(); i++) {
List<Integer> l = list.get(i);
Collections.sort(l);
String key = "";
//[ 2 3 4 ] 转为 "2,3,4"
for (int j = 0; j < l.size() - 1; j++) {
key = key + l.get(j) + ",";
}
key = key + l.get(l.size() - 1);
ans.put(key, "");
}
//根据逗号还原 List
List<List<Integer>> ans_list = new ArrayList<List<Integer>>();
for (String k : ans.keySet()) {
String[] l = k.split(",");
List<Integer> temp = new ArrayList<Integer>();
for (int i = 0; i < l.length; i++) {
int c = Integer.parseInt(l[i]);
temp.add(c);
}
ans_list.add(temp);
}
return ans_list;
}
- 还有一种无需去重复的 没有仔细研究。。。
public List<List<Integer>> combinationSum(int[] nums, int target) {
List<List<List<Integer>>> ans = new ArrayList<>();
Arrays.sort(nums);
if (nums[0] > target) {
return new ArrayList<List<Integer>>();
}
// 先初始化 ans[0] 到 ans[target],因为每次循环是更新 ans,会用到 ans.get() 函数,如果不初始化会报错
for (int i = 0; i <= target; i++) {
List<List<Integer>> ans_i = new ArrayList<List<Integer>>();
ans.add(i, ans_i);
}
for (int i = 0; i < nums.length; i++) {
for (int sum = nums[i]; sum <= target; sum++) {
List<List<Integer>> ans_sum = ans.get(sum);
List<List<Integer>> ans_sub = ans.get(sum - nums[i]);
//刚开始 ans_sub 的大小是 0,所以单独考虑一下这种情况
if (sum == nums[i]) {
ArrayList<Integer> temp = new ArrayList<Integer>();
temp.add(nums[i]);
ans_sum.add(temp);
}
//如果 ans.get(sum - nums[i])大小不等于 0,就可以按之前的想法更新了。
//每个 ans_sub[j] 都加上 nums[i]
if (ans_sub.size() > 0) {
for (int j = 0; j < ans_sub.size(); j++) {
ArrayList<Integer> temp = new ArrayList<Integer>(ans_sub.get(j));
temp.add(nums[i]);
ans_sum.add(temp);
}
}
}
}
return ans.get(target);
}