【矩阵路径】不知道回溯怎么写?进来看模板就对了!
这几天做了几道回溯算法的题目,发现理解递归关键步骤的结果很重要,试图摸索出一套模板,思考的方法都是搭建好框架,然后逐步细想。先让我们看看道简单的回溯算法题目,一步步慢慢来:
1.矩阵路径
题目描述:
我们一步步来分析:
首先用两个for循环找到起始位置,代码如下:
bool exist(vector<vector<char>>& board, string word) {
if(word.empty())
return true;
rows=board.size();
cols=board[0].size();
for(int i=0;i<rows;i++)
for(int j=0;j<cols;j++)
{
if(hasPathCore(board,word,i,j,0))
return true;
}
return false;
}
难理解的是hasPathCore这个回溯函数。
第一步,找出函数退出的条件(成功退出,失败退出)
失败的退出条件:越界,字符不匹配
if(row>=rows || col>=cols || row<0 || col<0 ||word[pathLength]!=board[row][col])
return false;
成功退出的条件:查找到底部(适用于绝大多数的回溯算法)
if(pathLength==word.size()-1)//查找到最后,或者已近查找过了返回真
return true;
第二步,写出下一步递归的条件
此时,我们已经正确查找到了以一个字符的位置,接下来对他的上下左右进行查找,我们可以把它想象为一棵4叉树,就是每个节点有4个子节点,而树的遍历我们最容易想到的就是递归,我们来大概看一下:
boolean dfs(char[][] board, char[] word, int row, int col, int pathLength) {
if (边界条件的判断) {
return;
}
一些逻辑处理
bool haspath;
//往右
haspath = dfs(board, word, i + 1, j, pathLength + 1)
//往左
||dfs(board, word, i - 1, j, pathLength + 1)
//往下
||dfs(board, word, i, j + 1, pathLength + 1)
//往上
||dfs(board, word, i, j - 1, pathLength + 1);
//上面4个方向,只要有一个能查找到,就返回true;
一些逻辑处理
return res;
}
第三步,添加逻辑处理
此时我们的代码已经有大致框架了,我们单独调出三种状态了来跟着代码跑一边,开始状态,中间失败状态,结束状态。
开始状态:找到第一个匹配的字符串,没问题。
中间失败状态:让dfs查找全部失败,即红色框框中。
此时代码逻辑变成回到黄框1,这就是回溯最重要的地方,接下来链接上黄框2执行其逻辑。
扫描二维码关注公众号,回复:
12395486 查看本文章
此时我们知道要防止其走回头路。并且,逻辑1和逻辑2操作都是相对的,入队和出队,更改和还原,理解关键的一点:让代码回溯到1的初始状态。我们在之间添加具体信息:
bool hasPathCore(vector<vector<char>>& board, string word, int row,int col,int pathLength)
{
if(row>=rows || col>=cols || row<0 || col<0 ||word[pathLength]!=board[row][col])
return false;
if(pathLength==word.size()-1)
//查找到最后,或者已近查找过了返回真
return true;
board[row][col]='\0';
//与之前的if语句对应,表示下个坐标为【i,j】的情况已近走过了,直接返回,防止接下来的语句搜索过程走回头路。
bool haspath=hasPathCore(board,word,row-1,col,pathLength+1)
||hasPathCore(board,word,row,col-1,pathLength+1)
||hasPathCore(board,word,row+1,col,pathLength+1)
||hasPathCore(board,word,row,col+1,pathLength+1);
//千万不能写pathLength++!!
board[row][col]=word[pathLength];
//因为只代表此次搜索过程中,该元素已访问过,当初始cow,col变化时,又开始了另一次搜索过程(假word=bfsa的情况辅助理解)
return haspath;
}
- board[row][col]=’\0’ 表示【row,col】已近走过,防止下一次走回头路
- board[row][col]=word[pathLength] 表示【row,col】周围查找全部失败,将【row,col】状态还原,防止下个起点查找到该点位出错。
结束状态:查找到最后一个字符串且全部成功,函数直接返回真,没问题。
第四步,恭喜你!
完整代码:
class Solution {
public:
int rows=0;
int cols=0;
bool exist(vector<vector<char>>& board, string word) {
if(word.empty())
return true;
rows=board.size();
cols=board[0].size();
for(int i=0;i<rows;i++)
for(int j=0;j<cols;j++)
{
if(hasPathCore(board,word,i,j,0))
return true;
}
return false;
}
bool hasPathCore(vector<vector<char>>& board, string word, int row,int col,int pathLength)
{
if(row>=rows || col>=cols || row<0 || col<0 ||word[pathLength]!=board[row][col])
return false;
if(pathLength==word.size()-1)//查找到最后,或者已近查找过了返回真
return true;
board[row][col]='\0'; //与之前的if语句对应,表示下个坐标为【i,j】的情况已近走过了,直接返回,防止接下来的语句搜索过程走回头路。
bool haspath=hasPathCore(board,word,row-1,col,pathLength+1)||hasPathCore(board,word,row,col-1,pathLength+1)||hasPathCore(board,word,row+1,col,pathLength+1)||hasPathCore(board,word,row,col+1,pathLength+1); //千万不能写pathLength++!!
board[row][col]=word[pathLength]; //因为只代表此次搜索过程中,该元素已访问过,当初始i j变化时,又开始了另一次搜索过程(假设word=bfsa的情况辅助理解)
return haspath;
}
};
其他的题目也是同理,希望对大家有帮助。
电话号码的字母组合
class Solution {
public:
vector<string> letterCombinations(string digits) {
vector<string> combinations;
if (digits.empty()) {
return combinations;
}
unordered_map<char, string> phoneMap{
{
'2', "abc"},
{
'3', "def"},
{
'4', "ghi"},
{
'5', "jkl"},
{
'6', "mno"},
{
'7', "pqrs"},
{
'8', "tuv"},
{
'9', "wxyz"}
};
string combination;
backtrack(combinations, phoneMap, digits, 0, combination);
return combinations;
}
void backtrack(vector<string>& combinations, const unordered_map<char, string>& phoneMap, const string& digits, int index, string& combination) {
if (index == digits.length())
{
combinations.push_back(combination);
}
else
{
char digit = digits[index];
const string& letters = phoneMap.at(digit);
for (const char& letter: letters)
{
combination.push_back(letter);
backtrack(combinations, phoneMap, digits, index + 1, combination);
combination.pop_back();
}
}
}
};