力扣 39 组合总和

力扣 39 组合总和

全部刷题与学习记录

【C++刷题学习笔记目录】

【C++百万并发网络通信-笔记目录】

原题目

题目地址:39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

考查知识点

回溯法、回溯法的起点


自己的第一遍解法

本来是一道组合题(结果不带顺序),因为回溯法的起点设置错误,做成了一道排列题(结果带顺序)

按照【算法套路】-【回溯篇】【回溯三步法】提到的回溯三步法,来分析一下这道题

作为回溯问题,需要两个数组来记录路径path与最终返回结果result,同时这道题还要求计算路径path内全部元素之和sum

1、回溯函数返回值及参数:返回值没什么好说的,就是void,参数首先要有题目所给的vector<int>& candidatesint target,其次还要有for循环开始的索引int index

2、回溯函数终止条件:有两种,1)sum == target,此时将path加入result;2)sum > target,此时直接回退

3、单层回溯逻辑:很显然for循环从起点开始,把当前遍历的数字加入path中,在累计加和;撤销的时候也要对应path弹出尾部元素,sum减去弹出的元素。下面是重点,我自己在写程序的时候,不自觉就把for循环写成了这样:

//跟着感觉盲写回溯
for (int i = index; i < candidates.size(); ++i) {
    
    //3:单层回溯逻辑
    path.push_back(candidates[i]);
    sum += candidates[i];
    backTracking(candidates, target, index);
    sum -= path.back();//回溯,撤销操作
    path.pop_back();
}
输出结果:
2 2 3 
2 3 2 
3 2 2 
7 
    
//正确答案
for (int i = index; i < candidates.size(); ++i) {
    
    //3:单层回溯逻辑
    path.push_back(candidates[i]);
    sum += candidates[i];
    backTracking(candidates, target, i);//不必是i+1,表示重复读取当前的数
    sum -= path.back();//回溯,撤销操作
    path.pop_back();
}
输出结果:
2 2 3 
7 

注意差别就在于backTracking()传入的最后一个index形参出了问题,实参传进去index是没有意识顺手写出来的,这样每次向下递归都是从[2,3,6,7]的第一个元素开始,最后的输出答案就是一个排列问题的答案,但我们是一个组合问题,本题可以出现重复元素,那么我们就从i的位置向下递归,也就是说,下次递归时,选择范围就要缩小到[i, 最后][开始,i-1]我们都是不考虑的,也就是搜索是向多叉树的右下角不断进行的。

一定要注意这个形参index的传入实参,如果是实参index,index在全程没有改变,那么每次为形参index传入的都是0,这样就是一个排列问题,如果传入的是i,那么每次向下递归,可供选择的范围就会缩小,这样才是一个回归问题。

本篇文章参考:代码随想录-回溯算法:求组合总和(二)

猜你喜欢

转载自blog.csdn.net/weixin_44484715/article/details/113746854