22. 括号生成

回溯三部曲
1、递归函数参数
cpp
vector<string> result;
string s;
void backtrack(int left, int right, int n)
left:当前已使用的左括号数量。
right:当前已使用的右括号数量。
n:括号的对数,也是左括号或右括号的最大数量。
2、回溯终止条件
cpp
if (left == n && right == n) {
res.push_back(s);
return;
}
3、单层搜索逻辑
在每一层递归中,有两个分支可以选择:添加一个左括号 '(' 或者添加一个右括号 ')'。但必须满足合法性条件(即数量约束)。
cpp
if(left<n){
s.push_back('(');
backtracking(left+1,right,n);
s.pop_back();
}
if(right<left){
s.push_back(')');
backtracking(left,right+1,n);
s.pop_back();
}
【注】
1、注意是right<left,不是right<n,因为任何时候,已放置的右括号数量必须小于等于左括号数量(且要保证先有左括号,最终两者相等)。
核心代码:
cpp
class Solution {
public:
vector<string> result;
string s;
void backtracking(int left, int right, int n){
if(left==n&&right==n){
result.push_back(s);
return;
}
if(left<n){
s.push_back('(');
backtracking(left+1,right,n);
s.pop_back();
}
if(right<left){
s.push_back(')');
backtracking(left,right+1,n);
s.pop_back();
}
}
vector<string> generateParenthesis(int n) {
result.clear();
s.clear();
backtracking(0,0,n);
return result;
}
};
79. 单词搜索(这道题有些不同)

回溯三部曲
1、递归函数参数
cpp
bool backtracking(vector<vector<char>>& board,string word,int i,int j,int index)
board:二维字符网格,需要被修改以标记已访问的格子(通过临时替换为特殊字符 '#')
word:目标单词
i, j:当前搜索位置的坐标。
index:当前待匹配的字符在 word 中的下标(从0开始)。当 index == word.size() 时表示所有字符都已匹配完成。
让回溯函数返回 bool 值,这样一旦找到就可以快速终止递归!!
2、回溯终止条件
cpp
if (index == word.size()) return true;
if (i < 0 || i >= board.size() || j < 0 || j >= board[0].size() || board[i][j] != word[index])
return false;
成功终止:当 index 等于单词长度时,说明之前已经匹配完所有字符
失败终止 :如果当前坐标越界,或者当前格子字符与待匹配字符 word[index] 不相等,则此路不通,返回 false。这既是边界检查,也是剪枝条件!
3、单层搜索逻辑
cpp
char temp = board[i][j];
board[i][j] = '#'; // 标记已访问
bool found = dfs(board, word, i + 1, j, index + 1)
|| dfs(board, word, i - 1, j, index + 1)
|| dfs(board, word, i, j + 1, index + 1)
|| dfs(board, word, i, j - 1, index + 1);
board[i][j] = temp; // 回溯,恢复原字符
return found;
为什么board[i][j] = '#';?
在单词搜索的 DFS 过程中,我们需要确保同一个格子不能在一次搜索路径中被重复使用。例如,如果从某个格子出发向四周探索,可能会再次回到这个格子(比如先向右走,下一步再向左回来),这样会导致路径中出现循环,或者错误地认为可以用同一个格子多次匹配单词的不同字符,而实际上每个格子只能用一次。
为了解决这个问题,在进入递归之前,我们临时将当前格子的字符修改为一个特殊标记(如 '#'),表示该格子已被当前路径占用。这样,在后续的递归中,如果试图访问这个格子,会因为字符不匹配(board[i][j] != word[index])而返回 false,从而避免了重复使用。
核心代码:
cpp
class Solution {
private:
bool backtracking(vector<vector<char>>& board,string word,int i,int j,int index){
if(index == word.size()) return true;
if(i<0||i>=board.size()||j<0||j>=board[0].size()||board[i][j]!=word[index]) return false;
char temp = board[i][j];
board[i][j] = '#';
bool found = backtracking(board,word,i+1,j,index+1)
||backtracking(board,word,i-1,j,index+1)
||backtracking(board,word,i,j+1,index+1)
||backtracking(board,word,i,j-1,index+1);
board[i][j] = temp;
return found;
}
public:
bool exist(vector<vector<char>>& board, string word) {
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if(backtracking(board,word,i,j,0)){
return true;
}
}
}
return false;
}
};
【注】
1、j>=board[0].size()
注意是>=就越界了!!!
2、跟前面的题目不同,不是在exist里直接
cpp
return backtracking(board, word, 0, 0, 0);
单词可能在网格的任意位置开始,但这里只从 (0,0) 开始搜索,如果单词起点在其他格子,就会漏掉正确答案。
正确做法是遍历所有格子作为起点,一旦某个起点返回 true,则整个函数返回 true。
51. N皇后
