题目描述
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:
输入:n = 1, k = 1
输出:[[1]]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combinations
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
真的就,忘了!
上次做,明明总结了很久,可惜这次做又忘了。
其实总体思路是有的,就是递归内,加多一个for循环,但是卡在哪呢?
- 卡在两个list如何添加的问题
- 卡在回溯回溯,回溯与递归最大的不同就是需要回溯。
就是有一步是返回的操作。
组合问题确实有点难想,就先记住组合最基本的问题,先记住这个,再去想变型吧。
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combine(int n, int k) {
combineHelper(n, k, 1);
return result;
}
/**
* 每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围,就是要靠startIndex
* @param startIndex 用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )。
*/
private void combineHelper(int n, int k, int startIndex){
//终止条件
if (path.size() == k){
result.add(new ArrayList<>(path));
return;
}
//for循环,这是组合问题的精髓, 就是for循环内有个 递归,然后for循环的条件就是起始坐标会变
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++){
//这个剪枝其实可以理解,但是还是背起来吧。每次都去推也挺麻烦的
//本来是n,现在是n减掉k——size再加1
path.add(i);属于是,要把i加入
combineHelper(n, k, i + 1);
path.removeLast();//而这一步就是整个回溯的重点吧,就是要返回!!!整个组合问题的精髓!!!
}
}
}
作者:carlsun-2
链接:https://leetcode-cn.com/problems/combinations/solution/dai-ma-sui-xiang-lu-dai-ni-xue-tou-hui-s-0uql/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
你懂吧,复制粘贴,,四舍五入等于我学会了。
刚刷了组合一,下不了手阿,没有思路,思考了很久。
遇到组合问题,第一步还是先回到最基础的组合模板来吧,就是上面的这道题。
辅助记忆
- 两个全局变量的list,在递归的时候加入
- 递归内
- 1.结束条件,就是list满了
- 2.for循环,起始下标要变。
- 3.其实整个函数的关键就只在于递归函数,只需要考虑递归函数就行了
- 整个递归函数的关键又在于for循环内,你可以把第一次就按照这个模板套
- 首先,第一个数得加进去吧。
- list.add
- 递归后,从1去找,找2,满了之后就把2弹出
- 继续找3,4,5
记住这个模板吧。不只是for循环和递归,还有list的增加删除,这才是回溯!!!
记得递归函数有个startindex,每一次for循环从startindex开始,for循环内的startindex要加1.
我是fw,只能记住模板了!!!
- 两个list
- 递归 startindex
- for循环 startindex开始
- 循环内 递归 startindex要加1
组合三:
题目描述
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
题解:
昨天下午看了一个多小时组合三想不出来,正常。因为怎么说,组合三还是构建在组合一的基础上,你组合1都忘了,还搞什么组合3嘛。
昨天把组合一过了一遍,今天的组合三简直醍醐灌顶。
确实,就照着模板来就好了。不过还是在一些小细节上出现了些许问题。
组合三的思路其实就是 建立在组合一上,每次判断一下组合内数的总和是不是等于k,是的话就输出。
而判断这里就是我出错的地方。原以为要传什么num进去,却一直解不出来,后来才发现确实不是一个好方法。那就按照笨方法来做阿。
就直接 对list内的元素for循环一遍加起来 看是否等于k喽。也不会很难滴。
public class demo15 {
List<List<Integer>>list1=new ArrayList<>();
LinkedList<Integer>list2= new LinkedList<>();//注意这里是Linklist,前后都得是Linklist
public List<List<Integer>> combinationSum3(int k, int n) {
dfs(k,n,1);
return list1;
}
void dfs(int k,int n,int startindex)
{
//结束条件
if (list2.size()==n) {
int tempSum = 0;
//本方法啦,就直接加强for循环加起来总和就好了
for (int num : list2) {
tempSum += num;
}
if (tempSum == k) {
list1.add(new ArrayList<Integer>(list2));
}
return;
}
//for循环嵌套
for(int i=startindex;i<=9;i++)
{
list2.add(i);
dfs(k,n,i+1);
list2.removeLast();
}
}
手动分割线,好激动,刚去力扣上看了题解,有大佬的思路和我一样,而我也知道自己错在哪了!!!
class Solution {
List<List<Integer>> res = new ArrayList<>(); // 用于存储全部结果
LinkedList<Integer> track = new LinkedList<>(); // 用于存储单次的返回结果
public List<List<Integer>> combinationSum3(int k, int n) {
backtrack(0, n, k, 1);
return res;
}
// 递归函数
public void backtrack(int curSum, int targetSum, int k, int start) {
// base case
if (track.size() == k) {
if (curSum == targetSum) {
res.add(new ArrayList<>(track));
}
return;
}
for (int i = start; i <= 9; i++) {
//减枝版本同样可以写
i<=9-(k-size)+1
// 做选择
track.add(i);
curSum += i;
// 递归遍历
backtrack(curSum, targetSum, k, i + 1);
// 回溯,撤销选择
//!!!我就是因为少了这句!!!难怪一直搞不出来!、
//确实阿!!!回溯回溯,这才是回溯的本质阿,前面加了,递归回溯之后
//也得减回来!!!
//只有减掉,才不对for循环中加入下一个i造成影响!!! 回溯回溯回溯的本质
//有进有出才是回溯
curSum -= i;
track.removeLast();
}
}
}
其实这道题还有另外一个做法,也挺好的。就是很多题目都会应用到的二进制解法。二进制解法,用01可以来表示很多事。
比如说这道题
用一个逆向思维,组合中含有1-9个数,
然后只有k的数的集合,也就是说1到9之间只能选K个
我们假设1到9个比特位,每一个比特位为1就代表那个位有取到,比如第1位,第2位,第4位为1,就代表 1 2 4 为
所以要做一个从0到2的9次方的for循环,就代表了9个位置,每个位置有没有选的情况。
然后最主要地就是要判断一个数它的二进制形式,哪几个位为1,哪几个位为0.
用&运算!!!&预算,都为1,为1.否则全为0.
所以用for循环,i从0到9,1<<i&mask那个原本的数。
为什么能实现呢?比如1<<1会等于 00000010 ,此时跟一个多位二进制的数相&,只有该位,也就是第二位,也就是第i+1位为1时,整个结果才为1,否则为0.所以让其相&,如果结果为0,就说明i+1为1.
是吧,学到了吧。让一个多位二进制数与1<<i 相&,就能得到第i+1位是否为1.
代码:
class Solution {
List<Integer> temp = new ArrayList<Integer>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> combinationSum3(int k, int n) {
dfs(1, 9, k, n);
return ans;
}
public void dfs(int cur, int n, int k, int sum) {
if (temp.size() + (n - cur + 1) < k || temp.size() > k) {
return;
}
if (temp.size() == k) {
int tempSum = 0;
for (int num : temp) {
tempSum += num;
}
if (tempSum == sum) {
ans.add(new ArrayList<Integer>(temp));
return;
}
}
temp.add(cur);
dfs(cur + 1, n, k, sum);
temp.remove(temp.size() - 1);
dfs(cur + 1, n, k, sum);
}
}