「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战」
39. 组合总和
题目描述
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
示例 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]]
示例 3:
输入: candidates = [2], target = 1
输出: []
示例 4:
输入: candidates = [1], target = 1
输出: [[1]]
示例 5:
输入: candidates = [1], target = 2
输出: [[1,1]]
解析
回溯法 回溯三部曲
- 1、确定递归函数与返回值
- 参数:
- (1)定义两个全局参数,二维数组
result用来存放结果,数组path用来存放符合条件的结果 - (2)题目给出了参数:候选元素数组
candates, 目标值target - (3)为了方便统计
path中存储的元素和,所以还需要一个int类型的sum来存放path中的和 - (4)同时还需要一个索引变量
startIndex来记录for循环中每次遍历开始的位置 综上,递归函数的参数有四个:candates , target , sum , startIndex
- (1)定义两个全局参数,二维数组
- 参数:
- 2、递归终止的条件
- 有两种情况需要终止递归:
- (1)sum的值等于 target的值,即 sum == target
- (2)sum的值大于 target的值,即 sum > target
- 有两种情况需要终止递归:
- 3、单层循环的逻辑
- 单层
for循环从startIndex开始,搜索candates集合 - 注意需要进行回溯
- 单层
代码
class Solution
{
public:
vector<vector<int>> combinationSum(vector<int> &candates, int target)
{
result.clear();
path.clear();
backtracking(candates, target, 0, 0);
return result;
}
private:
// 存放结果集
vector<vector<int>> result;
// 存放符合条件的元素
vector<int> path;
void backtracking(vector<int> &candates, int target, int sum, int startIndex)
{
// 终止条件
if (sum > target)
{
return;
}
if (sum == target)
{
result.push_back(path);
return;
}
// 遍历元素(未进行剪枝操作)
for (int i = startIndex; i < candates.size(); i++)
{
// 处理sum
sum += candates[i];
path.push_back(candates[i]);
// 这里元素可以重复使用,所以可以从i开始
backtracking(candates, target, sum, i);
// 回溯
path.pop_back();
sum -= candates[i];
}
}
};
剪枝优化
首先对candates进行排序,如果下一层的 sum (也就是本层的 sum+candates[i]),如果值大于 target ,就可以结束本轮for循环
剪枝后的完整代码
class Solution
{
public:
vector<vector<int>> combinationSum(vector<int> &candates, int target)
{
result.clear();
path.clear();
// 剪枝操作。首先进行排序
sort(candates.begin(), candates.end());
backtracking(candates, target, 0, 0);
return result;
}
private:
// 存放结果集
vector<vector<int>> result;
// 存放符合条件的元素
vector<int> path;
void backtracking(vector<int> &candates, int target, int sum, int startIndex)
{
// 终止条件
if (sum > target)
{
return;
}
if (sum == target)
{
result.push_back(path);
return;
}
// 剪枝优化
// 对总集合进行排序后,如果下一层的 sum (也就是本层的 sum + candates[i])
// 如果已经大于 target 就就可以结束本轮的for循环
// for 循环剪枝, 需要先对 candates 进行排序
for (int i = startIndex; i < candates.size() && sum + candates[i] <= target; i++)
{
// 下面的代码与剪枝前的相同
sum += candates[i];
path.push_back(candates[i]);
backtracking(candates, target, sum, i);
path.pop_back();
sum -= candates[i];
}
}
};