文章目录
- 引言
- 一、单词搜索
- 二、黄金矿工
- 三、不同路径 |||
- 四、图像渲染
- 五、岛屿数量
- 六、岛屿的最大面积
- 七、被围绕的区域
- 八、太平洋大西洋水流问题
- 九、扫雷游戏
- 总结
引言
在二维网络图中的dfs,反而一般不需要画决策树,因为在二维图像中有时候很直观可以看出决策关系,一般为上下左右搜索。
一、单词搜索
细节:
- dfs函数设置返回值bool,以便及时调整路线
- 设置向量数组dx,dy
- 设置bool数组vis,实现剪枝
- pos设置为函数参数,方便回溯
class Solution
{
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
bool vis[7][7];
int m, n;
public:
bool dfs(vector<vector<char>>& board, int i, int j, string& word, int pos)
{
if(pos == word.size()) return true;
vis[i][j] = true;
for(int k=0; k<4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n
&& !vis[x][y] && board[x][y] == word[pos])
{
if(dfs(board, x, y, word, pos + 1)) return true;
}
}
vis[i][j] = false;
return false;
}
bool exist(vector<vector<char>>& board, string& word)
{
m = board.size(), n = board[0].size();
for(int i=0; i<m; ++i)
{
for(int j=0; j<n; ++j)
{
if(board[i][j] == word[0])
{
if(dfs(board, i, j, word, 1)) return true;
}
}
}
return false;
}
};
二、黄金矿工
细节:
- 设置向量数组dx,dy
- 设置bool数组vis,实现剪枝
- sum设置为函数参数,方便回溯
class Solution
{
int ret = 0;
bool vis[16][16];
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int m, n;
public:
void dfs(vector<vector<int>>& grid, int i, int j, int sum)
{
vis[i][j] = true;
for(int k=0; k<4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n
&& !vis[x][y] && grid[x][y])
{
dfs(grid, x, y, sum + grid[x][y]);
}
else ret = max(ret, sum);
}
vis[i][j] = false;
}
int getMaximumGold(vector<vector<int>>& grid)
{
m = grid.size(), n = grid[0].size();
for(int i=0; i<m; ++i)
{
for(int j=0; j<n; ++j)
{
if(grid[i][j])
{
dfs(grid, i, j, grid[i][j]);
}
}
}
return ret;
}
};
三、不同路径 |||
细节:
- 设置向量数组dx,dy
- 设置bool数组vis,实现剪枝
- path设置为函数参数,方便回溯
class Solution
{
int ret = 0;
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
bool vis[20][20];
int m, n, count = 0;
public:
void dfs(vector<vector<int>>& grid, int i, int j, int path)
{
if(grid[i][j] == 2)
{
if(path + count == m * n) ++ret;
return;
}
vis[i][j] = true;
for(int k=0; k<4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n
&& !vis[x][y] && grid[x][y] != -1)
{
dfs(grid, x, y, path + 1);
}
}
vis[i][j] = false;
}
int uniquePathsIII(vector<vector<int>>& grid)
{
m = grid.size(), n = grid[0].size();
int si, sj;
for(int i=0; i<m; ++i)
{
for(int j=0; j<n; ++j)
{
if(grid[i][j] == -1) ++count;
else if(grid[i][j] == 1)
{
si = i, sj = j;
}
}
}
dfs(grid, si, sj, 1);
return ret;
}
};
- 本题开始往后,是floodfill算法的练习
- floodfill算法,本质就是寻找连通块
- 同时floodfill算法没有回溯
四、图像渲染
细节:
- 设置向量数组dx,dy
- 记录baseColor, newColor
class Solution
{
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int baseColor, newColor;
int m, n;
public:
void dfs(vector<vector<int>>& image, int i, int j)
{
image[i][j] = newColor;
for(int k=0; k<4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n
&& image[x][y] == baseColor)
{
dfs(image, x, y);
}
}
}
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color)
{
if(image[sr][sc] == color) return image;
baseColor = image[sr][sc], newColor = color;
m = image.size(), n = image[0].size();
dfs(image, sr, sc);
return image;
}
};
五、岛屿数量
细节:
- 设置向量数组dx,dy
- 设置bool数组vis,实现剪枝
class Solution
{
int ret;
bool vis[301][301];
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int m, n;
public:
void dfs(vector<vector<char>>& grid, int i, int j)
{
vis[i][j] = true;
for(int k=0; k<4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n
&& !vis[x][y] && grid[x][y] == '1')
{
dfs(grid, x, y);
}
}
}
int numIslands(vector<vector<char>>& grid)
{
m = grid.size(), n = grid[0].size();
for(int i=0; i<m; ++i)
{
for(int j=0; j<n; ++j)
{
if(!vis[i][j] && grid[i][j] == '1')
{
dfs(grid, i, j);
++ret;
}
}
}
return ret;
}
};
六、岛屿的最大面积
细节:
- 设置向量数组dx,dy
- 设置bool数组vis,实现剪枝
class Solution
{
int ret, sum;
vector<vector<bool>> vis;
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int m, n;
public:
void dfs(vector<vector<int>>& grid, int i, int j)
{
++sum;
vis[i][j] = true;
for(int k=0; k<4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n
&& !vis[x][y] && grid[x][y] == 1)
{
dfs(grid, x, y);
}
else ret = max(ret, sum);
}
}
int maxAreaOfIsland(vector<vector<int>>& grid)
{
m = grid.size(), n = grid[0].size();
vis.resize(m, vector<bool>(n));
for(int i=0; i<m; ++i)
{
for(int j=0; j<n; ++j)
{
if(!vis[i][j] && grid[i][j] == 1)
{
sum = 0;
dfs(grid, i, j);
}
}
}
return ret;
}
};
七、被围绕的区域
细节:
- 正难则反,将边界上及其相连的‘O’先全部标记起来,最后遍历矩阵,将未被标记的‘O’改成‘X’
- 设置向量数组dx,dy
- 设置bool数组vis,实现剪枝
class Solution
{
vector<vector<bool>> vis;
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int m, n;
public:
void dfs(vector<vector<char>>& board, int i, int j)
{
vis[i][j] = true;
for(int k=0; k<4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n
&& !vis[x][y] && board[x][y] == 'O')
{
dfs(board, x, y);
}
}
}
void solve(vector<vector<char>>& board)
{
m = board.size(), n = board[0].size();
vis.resize(m, vector<bool>(n));
for(int i=0; i<m; ++i)
{
if(board[i][0] == 'O') dfs(board, i, 0);
if(board[i][n-1] == 'O') dfs(board, i, n-1);
}
for(int j=0; j<n; ++j)
{
if(board[0][j] == 'O') dfs(board, 0, j);
if(board[m-1][j] == 'O') dfs(board, m-1, j);
}
for(int i=0; i<m; ++i)
{
for(int j=0; j<n; ++j)
{
if(!vis[i][j] && board[i][j] == 'O')
{
board[i][j] = 'X';
}
}
}
}
};
八、太平洋大西洋水流问题
思路:正难则反,水往高处流。
- 利用两个标记数组,分别记录太平洋和大西洋的水可以流到的区域,找出标记数组重叠的部分即可。
- 为了只用写一份dfs函数,将vis标记数组以参数的形式传递
- 设置向量数组dx,dy
class Solution
{
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
int m, n;
public:
void dfs(vector<vector<int>>& heights, int i, int j, vector<vector<bool>>& vis)
{
vis[i][j] = true;
for(int k=0; k<4; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n
&& !vis[x][y] && heights[x][y] >= heights[i][j])//正难则反,水往高处流
{
dfs(heights, x, y, vis);
}
}
}
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights)
{
m = heights.size(), n = heights[0].size();
vector<vector<bool>> vis1(m, vector<bool>(n));
vector<vector<bool>> vis2(m, vector<bool>(n));
for(int i=0; i<m; ++i)
{
if(!vis1[i][0]) dfs(heights, i, 0, vis1);
if(!vis2[i][n-1]) dfs(heights, i, n-1, vis2);
}
for(int j=0; j<n; ++j)
{
if(!vis1[0][j]) dfs(heights, 0, j, vis1);
if(!vis2[m-1][j]) dfs(heights, m-1, j, vis2);
}
vector<vector<int>> ret;
for(int i=0; i<m; ++i)
{
for(int j=0; j<n; ++j)
{
if(vis1[i][j] && vis2[i][j])
{
ret.push_back({i, j});
}
}
}
return ret;
}
};
九、扫雷游戏
细节:本题实际上是一道模拟题,关键是理解题意。
- 点击位置是雷,直接改变返回
- 不是雷,则进入dfs函数递归
- 对于当前格子,先计算周围8个格子中雷的数量
- 如果有雷,将当前格子改为雷的数量,返回
- 如果无雷,将当前格子改为挖出的空方块,继续递归
- 递归的时候,要进行8个方向的搜索,所以向量数组要存储8个方向
class Solution
{
bool vis[51][51];
int dx[8] = {1, 1, 1, 0, 0, -1, -1, -1};
int dy[8] = {1, 0, -1, 1, -1, 1, 0, -1};
int m, n;
public:
void dfs(vector<vector<char>>& board, int i, int j)
{
if(board[i][j] == 'M') return;
int count = 0;
for(int k=0; k<8; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n && board[x][y] == 'M')
{
++count;
}
}
if(count)
{
board[i][j] = count + '0';
return;
}
else board[i][j] = 'B';
vis[i][j] = true;
for(int k=0; k<8; ++k)
{
int x = i + dx[k], y = j + dy[k];
if(x >= 0 && y >= 0 && x < m && y < n && !vis[x][y])
{
dfs(board, x, y);
}
}
}
vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click)
{
int ci = click[0], cj = click[1];
if(board[ci][cj] == 'M')
{
board[ci][cj] = 'X';
return board;
}
m = board.size(), n = board[0].size();
dfs(board, ci, cj);
return board;
}
};
总结
网络图中的dfs,常用的搜索技巧,是创建向量数组dx,dy,方便简洁地用循环遍历。