39. 组合总和
典型的递归回溯问题。因为要考虑到要可重复的问题,所以每一次都要重复遍历整个数组,考虑到重复计算问题,需要加上记忆化搜索。
AC代码
class Solution {
List<List<Integer>> res = new LinkedList();
Map<String, Integer> memo = new HashMap<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
if (candidates.length == 0) {
return res;
}
Arrays.sort(candidates);
f(candidates, target, 0, new LinkedList<>());
return res;
}
void f(int[] candidates, int target, int start, LinkedList<Integer> current) {
if (target == 0) {
// 记忆化搜索,避免重复计算
if (memo.containsKey(generateKey(current))) {
return;
}
res.add(current);
memo.put(generateKey(current), 0);
return;
}
// 因为排序了,所以当当前的target比数组中最小的数还要小时可以认为没有满足的元素了,直接结束当前递归
if (target < candidates[0]) {
return;
}
for (int i = start; i < candidates.length; i++) {
current.addLast(candidates[i]);
// start == i表示下次计算仍从i位置开始,因为条件要求可以有重复元素
f(candidates, target - candidates[i], i, new LinkedList<>(current));
// 因为i位置的元素已经计算过了,后面再也不会用到了,可以删除
current.removeLast();
}
}
// 计算key的辅助函数
String generateKey(LinkedList<Integer> current) {
current.sort((a, b) -> a - b);
return current.toString();
}
}