【LeetCode】39. 组合总和
39. 组合总和
知识点:递归;回溯;组合;剪枝
题目描述
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
示例
输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
输入: candidates = [2], target = 1
输出: []
输入: candidates = [1], target = 1
输出: [[1]]
输入: candidates = [1], target = 2
输出: [[1,1]]
解法一:回溯
回溯算法的模板:
result = [] //结果集
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径) //把已经做出的选择添加到结果集;
return //一般的回溯函数返回值都是空;
for 选择 in 选择列表: //其实每个题的不同很大程度上体现在选择列表上,要注意这个列表的更新,
//比如可能是搜索起点和重点,比如可能是已经达到某个条件,比如可能已经选过了不能再选;
做选择 //把新的选择添加到路径里;路径.add(选择)
backtrack(路径, 选择列表) //递归;
撤销选择 //回溯的过程;路径.remove(选择)
核心就是for循环里的递归,在递归之前做选择,在递归之后撤销选择;
对于本题,有两点和77题组合不一样:
- 此题可以重复选取选过的元素,所以选择列表的搜索起点不用i+1,仍然是i。
- 此题没有像之前的题明确给出递归的层数,但是给了target,所以如果相加>target,那就证明到头了;
我们换个角度重新画这个图,和77题有点差距,理解的更全面一点。 其实这就是一个横向循环和纵向的递归,横向循环做出不同的选择,纵向在不同的选择基础上做下一步选择。
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
Stack<Integer> path = new Stack<>();
backtrack(candidates, target, 0, 0, res, path);
return res;
}
private void backtrack(int[] candidates, int target, int sum, int begin, List<List<Integer>> res, Stack<Integer> path){
if(sum > target){
return;
}
if(sum == target){
res.add(new ArrayList<>(path));
return;
}
for(int i = begin; i < candidates.length; i++){
//做选择;
sum += candidates[i];
path.push(candidates[i]);
//递归:开始下一轮选择;
backtrack(candidates, target, sum, i, res, path); //不用+1,可以重复选;
//撤销选择:回溯
sum -= candidates[i];
path.pop();
}
}
}
解法二:剪枝优化
上述程序有优化的空间,我们可以对数组先进行排序,然后如果找到了当前的sum已经等于target或大于target了,那后面的就可以直接跳过了,因为后面的元素更大,肯定更大于target。
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
Stack<Integer> path = new Stack<>();
Arrays.sort(candidates); //排序
backtrack(candidates, target, 0, 0, res, path);
return res;
}
private void backtrack(int[] candidates, int target, int sum, int begin, List<List<Integer>> res, Stack<Integer> path){
if(sum == target){
res.add(new ArrayList<>(path));
return;
}
for(int i = begin; i < candidates.length && sum + candidates[i] <= target; i++){
//剪枝:如果sum+candidates[i] > target就结束;
//做选择;
sum += candidates[i];
path.push(candidates[i]);
//递归:开始下一轮选择;
backtrack(candidates, target, sum, i, res, path); //不用+1,可以重复选;
//撤销选择:回溯
sum -= candidates[i];
path.pop();
}
}
}
体会
- 要能够把这种决策树画出来;
- 在求和问题中,排序之后加上剪枝是很常见的操作,能够舍弃无关的操作;
相关链接
【LeetCode】39. 组合总和的更多相关文章
- Java实现 LeetCode 39 组合总和
39. 组合总和 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字 ...
- [LeetCode] 39. 组合总和
题目链接 : https://leetcode-cn.com/problems/combination-sum/ 题目描述: 给定一个无重复元素的数组 candidates 和一个目标数 target ...
- [leetcode] 39. 组合总和(Java)(dfs、递归、回溯)
39. 组合总和 直接暴力思路,用dfs+回溯枚举所有可能组合情况.难点在于每个数可取无数次. 我的枚举思路是: 外层枚举答案数组的长度,即枚举解中的数字个数,从1个开始,到target/ min(c ...
- leetcode 39 组合总和 JAVA
题目: 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无限制 ...
- LeetCode 39. 组合总和(Combination Sum)
题目描述 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无限 ...
- leetcode 39. 组合总和(python)
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无限制重复被选 ...
- LeetCode——39. 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的数字可以无限制重复被选 ...
- Java实现 LeetCode 40 组合总和 II(二)
40. 组合总和 II 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的每个数字在 ...
- LeetCode 中级 - 组合总和II(105)
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的每个数字在每个组合中只能使用一次. ...
随机推荐
- keycloak~OIDC&OAuth2&自定义皮肤
1 OpenID & OAuth2 & SAML 1.1 相关资料 https://github.com/keycloak/keycloak https://www.keycloak. ...
- AcWing 1273. 天才的记忆
从前有个人名叫 WNB,他有着天才般的记忆力,他珍藏了许多许多的宝藏. 在他离世之后留给后人一个难题(专门考验记忆力的啊!),如果谁能轻松回答出这个问题,便可以继承他的宝藏. 题目是这样的:给你一大串 ...
- [心得体会]SpringMVC源码分析
1. SpringMVC (1) springmvc 是什么? 前端控制器, 主要控制前端请求分配请求任务到service层获取数据后反馈到springmvc的view层进行包装返回给tomcat, ...
- php结合redis实现高并发下的抢购、秒杀功能 (转)
抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易 ...
- 将gitlab内置node_exporter提供外部prometheus使用
目录 修改gitlab的配置 重新初始化配置 gitlab服务已经包含了node_exporter服务,但是配置文件限制了9100端口的访问,所以主机信息不能直接被外部的prometheus收集 修改 ...
- Python 操作 MySQL 的5种方式
不管你是做数据分析,还是网络爬虫,Web 开发.亦或是机器学习,你都离不开要和数据库打交道,而 MySQL 又是最流行的一种数据库,这篇文章介绍 Python 操作 MySQL 的5种方式,你可以在实 ...
- 痞子衡嵌入式:串行NOR Flash的页编程模式对于量产时间的影响
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是串行NOR Flash的页编程模式对于量产时间的影响. 任何嵌入式产品最终都绕不开量产效率话题,尤其是对于主控是非内置 Flash 型 ...
- Ubuntu虚拟机基本环境搭建以及参数设置
# 设置root账户密码 sudo passwd root # 修改源镜像(提高下载更新速度) sudo apt-get update pip install open-vm-tools pip in ...
- UI自动化测试框架Gauge 碰到无法识别Undefined Steps 红色波纹标记
如果碰到无法识别的情况,例如下面的红色波纹,可以试一下: 第一步: 第二步: 不勾选'offline work' 第三部:刷新之后可以重新编译.
- gpasswd简单记录
gpasswd [option] GROUP 一切都是为了权限 gpasswd常用参数: -a, --add USER 将user用户加入到组中 -d, --delete USER 将user用户 ...