前置知识
参考前文
216.组合总和III
题目描述
LeetCode链接:https://leetcode.cn/problems/combination-sum-iii/description/
解题思路
// 思路: 用回溯算法, 过程中记录当前使用了几个数字, 以及当前的sum和
代码
class Solution {
private:
vector<vector<int>> ans;
vector<int> path;
int sum;
public:
void helper(int k, int n, int curNum, int numNum){
if(numNum==k){
if(sum==n){
ans.push_back(path);
}
return;
}
for(int i=curNum; i<=9; ++i){
path.push_back(i);
sum += i;
helper(k, n, i+1, numNum+1);
path.pop_back();
sum -= i;
}
return;
}
vector<vector<int>> combinationSum3(int k, int n) {
helper(k, n, 1, 0);
return ans;
}
};
改进
① 函数命名用backtracking
② 不用记录numNum
的, 可以用path.size()
③ 可以不用sum
, 每一步用n-i
即可
④ 进行剪枝(当之前sum>n
, 或者现在n<0
时, 直接return
)
class Solution {
private:
vector<vector<int>> ans;
vector<int> path;
public:
void backTracking(int k, int n, int curNum){
if(n<0) return;
if(path.size()==k){
if(n==0){
ans.push_back(path);
}
return;
}
for(int i=curNum; i<=9; ++i){
path.push_back(i);
backTracking(k, n-i, i+1);
path.pop_back();
}
return;
}
vector<vector<int>> combinationSum3(int k, int n) {
backTracking(k, n, 1);
return ans;
}
};
17.电话号码的字母组合
题目描述
LeetCode链接:https://leetcode.cn/problems/letter-combinations-of-a-phone-number/description/
解题思路
思路: 用回溯算法, 每一位数字是一层回溯
用unordered_map<char,string>
存储电话按键符号和对应的string
每一层回溯函数的参数记录自己当前操作的是第几位数字
代码
class Solution {
unordered_map<char,string> phoneMap{
{'2', "abc"},
{'3', "def"},
{'4', "ghi"},
{'5', "jkl"},
{'6', "mno"},
{'7', "pqrs"},
{'8', "tuv"},
{'9', "wxyz"}
};
vector<string> ans;
string cur;
public:
void backtrack(const string& digits, int vec){
if(cur.size()==digits.size()){
ans.push_back(cur);
return;
}
string letters = phoneMap[digits[vec]];
for(int i=0; i<letters.size(); ++i){
cur.push_back(letters[i]);
backtrack(digits, vec+1);
cur.pop_back();
}
return;
}
vector<string> letterCombinations(string digits) {
if(digits.size()==0)
return ans;
backtrack(digits, 0);
return ans;
}
};
总结
今天的两道题虽然都套用了昨天说到的"回溯模板", 但是具体的操作有所区别, 我们可以对比总结为:
当需要"每个数字至多使用一次"的时候, 就需要每次从i=index
开始循环;
当"简单遍历循环所有可能"的时候, 需要i=0
开始循环.
同时在使用回溯算法的过程中, 有一些trick:
① 对于过程中使用的ans
和cur
, 虽然也可以以引用的方式写在回溯函数参数里面, 但是写在private
部分, 可以让整体更为清爽;
② 固定一些函数和参数的命名, 可以更好地帮助自己理清回溯的思路, 如backtrack
, ans
, path
, index
;
③ 过程中无需改变的量, 可以设置为const
.