回溯三部曲:
- 确定回溯参数
- 确定终止条件
- 确定单层遍历逻辑
17. 电话号码的字母组合
解法1: 回溯
class Solution {
public:
string tmp;
vector<string> board = {"", "", "abc", "def", "ghi","jkl","mno","pqrs","tuv","wxyz"};
vector<string> ans;
void dfs(int pos, string digits){
if(pos == digits.size()){
ans.push_back(tmp);
return;
}
int num = digits[pos] - '0'; //表示按到了键盘上的第几个键
for(int i = 0; i < board[num].size(); i ++){
tmp.push_back(board[num][i]);
dfs(pos + 1, digits); //递归下一层
tmp.pop_back(); //回溯
}
}
vector<string> letterCombinations(string digits) {
if(digits.size() == 0) return {};
dfs(0, digits);
return ans;
}
};
39. 组合总和
解法1: 回溯
class Solution {
public:
void dfs(vector<int>& candidates, int target, vector<vector<int>>& ans, vector<int>& combine, int idx) {
if (idx == candidates.size()) {
return;
}
if (target == 0) {
ans.emplace_back(combine);
return;
}
// 直接跳过
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
combine.emplace_back(candidates[idx]);
dfs(candidates, target - candidates[idx], ans, combine, idx);
combine.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> ans;
vector<int> combine;
dfs(candidates, target, ans, combine, 0);
return ans;
}
};
46. 全排列
解法1: 回溯
class Solution {
public:
void backtrack(vector<vector<int>>& res, vector<int>& output, int first, int len){
// 所有数都填完了
if (first == len) {
res.emplace_back(output);
return;
}
for (int i = first; i < len; ++i) {
// 动态维护数组
swap(output[i], output[first]);
// 继续递归填下一个数
backtrack(res, output, first + 1, len);
// 撤销操作
swap(output[i], output[first]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int> > res;
backtrack(res, nums, 0, (int)nums.size());
return res;
}
};
解法2: C++库函数
- 直接调用C++库next_permutation
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
do {
result.emplace_back(nums);
} while (next_permutation(nums.begin(), nums.end()));
return result;
}
};
78. 子集
解法1: 回溯
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex) {
result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
if (startIndex >= nums.size()) { // 终止条件可以不加
return;
}
for (int i = startIndex; i < nums.size(); i++) {
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
result.clear();
path.clear();
backtracking(nums, 0);
return result;
}
};
- 时间复杂度: O(n * 2^n)
- 空间复杂度: O(n)