下面整理一下排列搜索类的回溯算法,该类题目数组中的元素需要重复访问,但访问结果构成的集合又不能重复,因此需要用visited数组标记该元素有没有被访问过,这样在下一次搜索时会避开。
LeetCode46. 全排列 && LeetCode47. 全排列 II
LeetCode46给定一个 没有重复 数字的序列,LeetCode47(剑指 Offer 38. 字符串的排列也与之类似,只不过变换成了字符形式)则要求给出可重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
在访问该元素,即path.push_back(nums[i])后,将访问为置位true,path.pop_back()撤销选择访问结束时,将访问为置位false,两题做法完全一样。
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> permute(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<bool> visited(nums.size(), false);
dfs(nums, visited, 0);
return res;
}
void dfs(vector<int> nums, vector<bool> visited, int n){
if(n == nums.size()){
res.push_back(path);
return ;
}
for(int i = 0; i < nums.size(); i++){
if(visited[i]) continue;
if(i > 0 && nums[i] == nums[i - 1] && visited[i - 1] == true) continue;
//剪枝跳过重复元素
path.push_back(nums[i]);
visited[i] = true; //标志为已访问过
dfs(nums, visited, n + 1);
path.pop_back();
visited[i] = false; //访问结束重置标志位
}
}
};
LeetCode剑指 Offer 13. 机器人的运动范围
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
示例 1:
输入:m = 2, n = 3, k = 1
输出:3
示例 2:
输入:m = 3, n = 1, k = 0
输出:1
搜索类的回溯算法,需要设置 visited数组标记该元素有没有被访问过,由于机器人可以朝四个方向移动,需要设置横向和纵向的移动数组,接下来在递归函数里,首先将访问为置位true,然后沿着四个方向进行搜索即可,满足条件则计数。
class Solution {
public:
int count = 0;
vector<int> dx = {0, 0, 1, -1};
vector<int> dy = {1, -1, 0, 0};
int movingCount(int m, int n, int k) {
vector<vector<bool>> visited(m, vector<bool>(n, false));
if(k == 0) return 1;
dfs(m, n, k, visited, 0, 0);
return count + 1; //加1是因为没有计算起点
}
void dfs(int m, int n, int k, vector<vector<bool>>& visited, int i, int j){
visited[i][j] = true;
//四个方向进行搜索
for(int index = 0; index < 4; index++){
int x = i + dx[index];
int y = j + dy[index];
int sum = x%10 + x/10 + y%10 + y/10;
if(x < m && x >= 0 && y < n && y >= 0 && sum <= k && visited[x][y] == false){
//搜索满足条件:下标不越界,数位和小于k,节点未被访问过
count++;
dfs(m, n, k, visited, x, y);
}
}
}
};
LeetCode79. 单词搜索 && 剑指 Offer 12. 矩阵中的路径
给定一个二维网格和一个单词,找出该单词是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
给定 word = "ABCCED", 返回 true
给定 word = "SEE", 返回 true
给定 word = "ABCB", 返回 false
我们需要循环遍历矩阵,当出现首字母的元素时,开始进行搜索回溯,搜索的过程和LeetCode剑指 Offer 13. 机器人的运动范围一样,满足条件我们递归搜索单词的下一个字母,不同的是我们四个方向搜索完之后,需要将访问为置为未访问visited[i][j] = false,这是因为:我们是从某一个点开始搜索的,搜索过程中匹配的字符还可以作为其他情况的搜索起点或搜索过程点。
class Solution {
public:
int row, column;
vector<int> dx = {0, 0, 1, -1};
vector<int> dy = {1, -1, 0, 0};
bool dfs(vector<vector<char>>& board, int i, int j, string word, int index, vector<vector<bool>>& visited){
if(index == word.size()) return true;
visited[i][j] = true;
for(int k = 0; k < 4; k++){
int x = i + dx[k];
int y = j + dy[k];
if((x >= 0 && x < row) && (y >= 0 && y < column) && board[x][y] == word[index] && visited[x][y] == false){
if(dfs(board, x, y, word, index + 1, visited)) return true;
}
}
visited[i][j] = false; //回溯,访问位置未访问
return false;
}
bool exist(vector<vector<char>>& board, string word) {
if(board.size() == 0 || board[0].size() == 0) return false;
row = board.size();
column = board[0].size();
vector<vector<bool>> visited(row, vector<bool>(column, 0));
for(int i = 0; i < row; i++){
for(int j = 0; j < column; j++){
if(board[i][j] == word[0]){
if(dfs(board, i, j, word, 1, visited)) return true;
}
}
}
return false;
}
};
LeetCode200. 岛屿数量
给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。
示例:
输入:
[
['1','1','0','0','0'],
['1','1','0','0','0'],
['0','0','1','0','0'],
['0','0','0','1','1']
]
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。
此题思路和单词搜索类似,特殊的处理在于当我们搜索到岛屿时,我们进入递归函数,将当前节点置位0,因为岛屿是相连的,相当于我们去访问岛屿上的陆地,并将这些陆地置位访问过的标志,这样在主函数循环时,会去搜索新岛屿的陆地。
class Solution {
public:
vector<int> dx = {-1, 0, 1, 0},
vector<int> dy = {0, 1, 0, -1};
int numIslands(vector<vector<char>>& grid) {
int res = 0;
for (int i = 0; i < grid.size(); i ++){
for (int j = 0; j < grid[0].size(); j ++){
if (grid[i][j] == '1') {
res++;
dfs(grid, i, j);
}
}
}
return res;
}
void dfs(vector<vector<char>>& grid, int x, int y){
grid[x][y] = '0';
for (int i = 0; i < 4; i ++) {
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && b >= 0 && a < grid.size() && b < grid[0].size() && grid[a][b] == '1')
dfs(grid, a, b);
}
}
};