回溯是一种通过穷举所有可能情况来找到所有解的算法。如果一个候选解最后被发现并不是可行解,回溯算法会舍弃
它,并在前面的一些步骤做出一些修改,并重新尝试找到可行解。回溯算法一般会结合在搜索算法中。
1. 电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
来源:力扣(LeetCode)
解析:
找到当前数字对应的字符串映射;
用之前的凭借的字符串继续拼接当前映射中的每一个字符;
最后一次,保存组合。
class Solution {
public:
void DFS(int depth, string digits,string curStr,vector<string>& stringArr)
{
static unordered_map<char,string> digitsMap={{'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"},{'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}};
if(depth == digits.size())
{
stringArr.push_back(curStr);
return;
}
string digitMap=digitsMap[digits[depth]];
//digits[depth]
for(auto& e : digitMap)
{
DFS(depth+1,digits,curStr+e,stringArr);
}
}
vector<string> letterCombinations(string digits) {
vector<string> stringArr;
if(digits.empty())
return stringArr;
DFS(0,digits,"",stringArr);
return stringArr;
}
};
2. 组合总数
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
来源:力扣(LeetCode)
解析:
从最后一个累加的元素位置继续累加,避免重复
直到累加和大于等于目标值的时候,回退。如果等于目标,保存结果
class Solution {
public:
void DFS(vector<vector<int>>& Mat, vector<int>& Arr, int target, int curSum, vector<int>& candidates, int prePos)
{
if(curSum>=target)
{
if(curSum==target)
{
Mat.push_back(Arr);
}
return;
}
for(int i = prePos; i<candidates.size(); i++)
{
Arr.push_back(candidates[i]);
DFS(Mat,Arr,target,curSum+candidates[i],candidates,i);
Arr.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<int> Arr;
vector<vector<int>> Mat;
DFS(Mat,Arr,target,0,candidates,0);
return Mat;
}
};
3. N皇后
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。(不能摆放在同一行,同一列,同一斜线)
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
来源:力扣(LeetCode)
解析:
尝试当前行的每一列,判断当前位置是否和已经确定的皇后位置冲突,
不冲突:保存位置,继续确定下一行,
冲突:回溯,尝试其他位置
同行:行相同,但是不会同行
同列:列相同
正斜线:坐标差相同
反斜线:坐标和相同
class Solution {
public:
bool IsValid(vector<pair<int,int>>& sigleMethod,int row,int col)
{
for(pair<int,int> &i : sigleMethod)
{
//同一列 ,同一反斜线,同一斜线
if(i.second == col || i.first+i.second == row+col || i.first-i.second == row-col)
return false;
}
return true;
}
void DFS(vector<vector<pair<int,int>>>& allMethod, vector<pair<int,int>>& sigleMethod, int n, int curRow)
{
if(curRow == n)
allMethod.push_back(sigleMethod);
//尝试当前行的每一个位置
for(int col = 0; col < n; ++col)
{
if(IsValid(sigleMethod,curRow,col))
{
sigleMethod.emplace_back(curRow,col);
DFS(allMethod,sigleMethod,n,curRow+1);
sigleMethod.pop_back();//回溯
}
}
}
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> finalRet;
vector<vector<pair<int,int>>> allMethod;
vector<pair<int,int>> sigleMethod;
DFS(allMethod,sigleMethod,n,0);
//转换结果,把所有的方法转换成字符串
//遍历每一种方法
for(auto& curMethod : allMethod)
{
vector<string> stringMethod(n,string(n,'.'));
//把皇后的位置改为Q
for(auto& curPos:curMethod)
{
stringMethod[curPos.first][curPos.second]='Q';
}
finalRet.push_back(stringMethod);
}
return finalRet;
}
};