2022-03-10 17:23:59 +08:00

70 lines
2.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: "202203101"
date: "2022/03/10 20:15"
title: "Q39 组合总和(combination-sum)"
tags: ["java", "leetcode", "dfs"]
index_img: https://qiniupic.fleyx.com/blog/202203101631050.jpg?imageView2/2/w/200
banner_img: https://qiniupic.fleyx.com/blog/202203101631050.jpg
categories:
- "算法"
- "leetcode刷题"
---
<span id="blogIdSpan" style="display:none">202203101</span>
## 解析思路
leetcode 中等难度,题目描述[点击这里](https://leetcode-cn.com/problems/combination-sum/)。
标准的回溯类题目,对于回溯类题目,通常都是要穷举所有的情况,然后判断那些情况是符合题目要求的。
然后穷举通常是通过`深度优先搜索(dfs)`来实现的,我们可以先将结果展开成一棵树,然后再根据这棵树来写代码,就比较好理解,如下图:
以[2,3,5],8 为例:
![展开](https://qiniupic.fleyx.com/blog/202203101700108.png)
上图只对最左边的路径进行了完全展开(全部展开太麻烦了)
总的来说回溯就是不断的进行尝试,如果尝试到最后发现不满足要求那就换一个路径继续尝试,属于暴力算法。因此此类题目的数据规模通常会限制的比较小。
另外通常还能根据题目的要求来做剪枝操作,减少一些不必要的运算。
比如本题可以先将数组排序,当选择的某个数已经大于目标值时,就没必须选择这个数的下一个数继续尝试了。
## 代码
```java
public class Q39 {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
//排序
Arrays.sort(candidates);
dfs(candidates, target, 0, new Stack<>(), res);
return res;
}
private void dfs(int[] candidates, int target, int index, Stack<Integer> temp, List<List<Integer>> res) {
if (target == 0) {
//说明找到一个结果序列
res.add(new ArrayList<>(temp));
return;
}
for (int i = index; i < candidates.length; i++) {
if (candidates[i] > target) {
//前面已经排序过所以在这里可以进行剪枝操作如果candidates[index]都小于target了那就不需要比较后面的了肯定不满足要求
return;
}
temp.push(candidates[i]);
//注意这里为了让结果集不重复选择重复元素时只能对当前元素进行重复选择不能重复选择之前的元素。所以递归的index为i,不是0
dfs(candidates, target - candidates[i], i, temp, res);
temp.pop();
}
}
public static void main(String[] args) {
new Q39().combinationSum(new int[]{2, 3, 5}, 8).forEach(System.out::println);
}
}
```