链接:
https://leetcode.com/problems/combination-sum/
大意:
给定一个不含重复数字的数组c,以及一个目标和t(c中所有元素以及t都是正数)。在数组c中任意选任意个数字的组合,要求组合的和为t。其中,c中每个数可以被选择多次。例子:
思路:
回溯法。
首先看一个特殊情况:如果数组c的长度为0,则自然返回的是空列表了。
对数组c进行排序,为了之后更方便进行dfs
对于每一次选择数字,记录当前已选择过所有的数字的和subSum,当前选择元素在数组中的位置index,当前已选择的数的列表list,总的用于保存所有组合的列表res
那么可以分为以下几种情况讨论:
若subSum + c[index] < t 表明c[index]是可能加进当前list的(因为后面可能有值再加进来能满足等于t)
若subSum + c[index] == t 表明之前的list在添入当前元素c[index]后,刚好可以保证和为t。因此把当前元素添加到list,再把list加入到res,最后list移除刚添加的元素(回溯),再返回(因为数组没有重复的数字,若前n-1个数字都是固定了,则要使前n个数字的和为t,则第n个数字唯一)
若subSum + c[index] > t 表明之前list中元素的和加上当前元素值后已经大于t了,则也就没必要再找index之后的数了(因为c是按从小到大排序了的),所以直接return
代码:
class Solution {
public List<List<Integer>> combinationSum(int[] c, int t) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
if (c.length == 0)
return res;
Arrays.sort(c); // 首先对c排序 利于之后的dfs
dfs(c, t, 0, 0, res, new LinkedList<>());
return res;
}
// c:数组 t:目标和 subSum:当前阶段已选择数字的和 index:当前访问的数组中的位置 res:用于存储List的List list:一个临时List 用于存储每次的可能组合值
public void dfs(int[] c, int t, int subSum, int index, List<List<Integer>> res, LinkedList<Integer> list) {
// 带剪枝的回溯
for (int i = index; i < c.length; i++) {
if (subSum + c[i] < t) {
list.addLast(c[i]);
dfs(c, t, subSum + c[i], i, res, list);
list.removeLast(); //回溯
} else if (subSum + c[i] == t) {
list.addLast(c[i]);
res.add(new ArrayList<>(list));
list.removeLast();
return ;
} else {
return ;
}
}
}
}
结果:
结论:
很基础的一个回溯题,掌握了基本思路以及回溯的模板差不多就能解决了。优化的话,就是剪枝了