“持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情”
前言
之前小六六一直觉得自己的算法比较菜,算是一个短板吧,以前刷题也还真是三天打鱼,两台晒网,刷几天,然后就慢慢的不坚持了,所以这次,借助平台的活动,打算慢慢的开始开刷,并且自己还会给刷的题总结下,谈谈自己的一些思考,和自己的思路等等,希望对小伙伴能有所帮助吧,也可以借此机会把自己短板补一补,希望自己能坚持下去呀
题目
组合总和 III 216题
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
复制代码
分析
其实大家可以看看这题,也是用回溯算法解决的,最近几天,六六分享的都是回溯算法的,关于回溯算法的定义啥的,可以看上面那篇文章,这边我们就直接把模版拿出来,大家就像填词一样,往里面填代码试试,
模版
回溯三部曲
- 1.确定回溯单层逻辑
通过for循环控制当前位置(也就是当前递归层)的数字可以从1选到9,每选择一个数字(for每遍历到一个数字)我们就从目标总和中减去它的值,同时把他加入记录数字组合的集合中; 通过上一步我们就确定了第一位数字,接下来通过递归确定下一位数字,要注意的是需要更新下一层递归选择下一位数字时开始的位置,因为上一层选过的数字下一层就不能再选了; 当递归到某层时,我们发现此时已经确定了k个数字了,需要判断这k个数的和是否等于n,如果等于n,则把这次收集到的组合加入最终的结果集并终止当前层递归,准备回溯;反之,直接终止递归,准备回溯; 当我们从上一层递归中出来后,就该回溯了,即删掉最后一位的数字重新选择一个新数字;
- 2.确定回溯函数参数以及返回值
根据上述分析,可以得知我们在每层的递归中都会更新目标总和的值、判断当前是否选够了k个数字、以及我们必须直到当前这层递归中选择数字的起点在哪里 因此参数需要:目标总和、题目给出的k、当前层递归选择数字的起始位置 因为我们将用两个全局变量记录当前组合以及最终结果集,所以不需要返回值
- 3.确定回溯函数终止条件
在分析单层逻辑时,我们就已经得到:当我们选取了k个数字后们就一个终止当前递归返回了
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
复制代码
题解
package com.six.finger.leetcode.five;
/*
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。
*/
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class fifteen {
public static void main(String[] args) {
combinationsum(9, 45);
}
public static List<List<Integer>> combinationsum(int k, int n) {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
backtracking(res, path, k, n, 1);
return res;
}
private static void backtracking(List<List<Integer>> res, LinkedList<Integer> path, int k, int n, int i) {
int sum = 0;
//第一步,递归的退出条件
for (Integer integer : path) {
sum = sum + integer;
}
if (sum > n) {
return;
}
if (sum == n && path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
//第二步 树的横向长度,和减枝活动
for (int j = i; j <= 9 - (k - path.size()) + 1; j++) {
path.add(j);
backtracking(res, path, k, n, j + 1);
path.removeLast();
}
}
}
复制代码
一看提交记录,我去太菜了,想想怎么优化下
优化
其实就是,我为啥每次都要计算path的和,改下,把这样计算
for (int j = i; j <= 9 - (k - path.size()) + 1; j++) {
sum=sum+i;
path.add(j);
backtracking(res, path, k, n, j + 1,sum);
path.removeLast();
sum=sum-i;
}
复制代码
然后并没有什么用哦
结束
好了,今天这题就结束了,我发现连续的去练一种类型,其实这种题就慢慢的没那么难了,以前我总觉得算法很难,但是其实万事万物,都有其自然的规律,我们只能善于总结,虽然创造不出什么新的东西,但是还是可以把别人的经验学习到的,加油!!