前言:
如果你一点也不了解什么叫做回溯算法,那么推荐你看看这一篇回溯入门,让你快速了解回溯算法的基本原理及框架
电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
来源:力扣(LeetCode)电话号码的字母组合
思路:
从示例来看,我们首先能想到的最直接的思路就是循环套循环来组合输出,但是循环会根据输入的数字越来越多导致套的层次越多,这时候我们就可以考虑学习上一篇的思路,使用递归回溯的办法解决。
- 解决数字与字母映射的关系:
我们可以直接定义一个数组,下标对应其字母:
vector<string> arr{
"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz" };
//写两个空串是为了能用下标直接对应九宫格里的字母
- 1
- 2
- 3
- 画图举例
我们以"234"来说明
下面我们来边写边解释:
class Solution {
public:
vector<string> arr{
"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
vector<string> _arr;//收集所有子集
string tmp;//存储子集
void BackTracking(string digits,int k)//k是记录遍历到第几个数字
{
if(tmp.size()==digits.size())//终止条件
{
_arr.push_back(tmp);//存放子集到总集合
return;
}
//digits[k]-'0'是将数字字符改成数字
//arr[digits[k]-'0']访问数字字符对应下标位置的字符串
for(int i=0;i<arr[digits[k]-'0'].size();i++)
{
tmp.push_back(arr[digits[k]-'0'][i]);//收集字符
BackTracking(digits,k+1);//递归,k+1为下一次进入的字符串
tmp.pop_back();//回溯,撤销节点
}
}
vector<string> letterCombinations(string digits) {
if(digits.size()==0)
{
return _arr;
}
BackTracking(digits,0);
return _arr;
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
组合总和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
来源:力扣(LeetCode)组合总和
思路:
本题的主要关键信息是:所有数字不相同且可以无限制使用,组合与组合不相同,提示中已经表明不会出现0,所以我们直接忽略0带来的结果。
- 我们以示例2来[2,3,5]画图:
我们将candidates为升序排列,
由于数字的数量不做限制,我们每次for循环开始的位置就可以是上次循环变量i所在的位置,而递归的深度限制就是总和达到target或者大于它的时候
下面我们边写边解释:
class Solution {
public:
vector<vector<int>> arr;//收集符合条件子集
vector<int> _arr;//存放单个子集
//sum为_arr子集的总和数 begin为循环开始位置
void BackTracking(vector<int>& candidates,int target,int sum,int begin)
{
if(sum>target)//终止条件
{
return;
}
if(sum==target)
{
arr.push_back(_arr);
return;
}
for(int i=begin;i<candidates.size();i++)
{
sum+=candidates[i];//递加总和
_arr.push_back(candidates[i]);//存放至子集
BackTracking(candidates,target,sum,i);//i不加1 表示可以重复取该数
sum-=candidates[i];//回溯,撤销上一次的运算
_arr.pop_back();//回溯,撤销上一次的运算
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());//先排序,后续更好优化
BackTracking(candidates,target,0,0);
return arr;
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
剪枝优化
优化后的代码:
class Solution {
public:
vector<vector<int>> arr;
vector<int> _arr;
void BackTracking(vector<int>& candidates,int target,int sum,int begin)
{
if(sum==target)
{
arr.push_back(_arr);
return;
}
//如果sum+candidates[i]>target遍历可以停止了
for(int i=begin;i<candidates.size() && sum+candidates[i]<=target;i++)
{
sum+=candidates[i];
_arr.push_back(candidates[i]);
BackTracking(candidates,target,sum,i);
sum-=candidates[i];
_arr.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
BackTracking(candidates,target,0,0);
return arr;
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
这个题相较与回溯入门中的两个组合总和题
主要区别是没有组合里元素数量限制和单个元素数量限制