五、太平洋大西洋水流问题
题目解析

给定一个二维数组 heights、其中 heights[i][j] 表示该单元格的高度;
当 附近相邻单元格的高度,小于等于当前单元格高度时,当前单元格雨水可以流向附近单元格;(雨水可以从边缘单元格流向海洋)
要我们找出 既可以流向太平洋(左、上部分)、又可以流向大西洋(右、下部分)的单元格。
算法思路
简单来说这道题就是 要找出 即与左、上部分相连,又与右、下部分相连的所有单元格。
要直接去遍历,判断每一个单元格是否可以流向左上、右下部分;那实在太麻烦了;
这里我们就可以从边缘单元格开始遍历,记录每一个连通(可以流向边缘)的单元格,最后找出及可以流向左上部分、又可以流向右下部分的单元格即可。
代码实现
cpp
class Solution {
int dx[4] = {-1,1,0,0};
int dy[4] = {0,0,-1,1};
public:
void dfs(vector<vector<int>>& heights, int i, int j,vector<vector<bool>>& vis)
{
vis[i][j] = true;
int m = heights.size();
int n = heights[0].size();
for(int k =0; k<4;k++)
{
int x = i + dx[k];
int y = j + dy[k];
if(x>=0 &&x<m && y>=0 && y<n && !vis[x][y] && heights[i][j] <= heights[x][y])
dfs(heights,x,y,vis);
}
}
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
int m = heights.size();
int n = heights[0].size();
vector<vector<bool>> pac(m,vector<bool>(n,false));
vector<vector<bool>> atl(m,vector<bool>(n,false));
// 上 下
for(int i = 0;i<n;i++)
{
if(!pac[0][i]) dfs(heights,0,i,pac);
if(!atl[m-1][i]) dfs(heights,m-1,i,atl);
}
// 左 右
for(int i = 0;i<m;i++)
{
if(!pac[i][0]) dfs(heights,i,0,pac);
if(!atl[i][n-1]) dfs(heights,i,n-1,atl);
}
vector<vector<int>> ret;
for(int i = 0;i<m;i++)
{
for(int j = 0;j<n;j++)
{
if(pac[i][j] && atl[i][j])
ret.push_back({i,j});
}
}
return ret;
}
};
六、扫雷游戏
题目解析

扫雷游戏,作为一个经典的小游戏,具体规则这里就就不详细叙述了;看这道题的要求
给一个 m*n 的棋盘;M代表 为挖出的雷、E代表未挖出的方块、B代表相邻位置没有雷的已挖出的方块、数字1-8表示该位置附近雷的个数、X代表已经挖出的地雷。
给定一个位置[click[0],click[1]],返回该位置被点击后对应的盘面。
具体规则:
- 地雷
M被挖出,游戏结束,把它修改成X - 该位置是附近没有地雷的空方块
E,修改它为B,并将与其相邻的所有未挖出的方块都递归揭露(修改成B,或者1-8) - 如果该位置至少与一个地雷相邻,则修改为数字
1-8 - 如果该位置已经被揭露,则直接返回
算法思路
读懂了这道题,仔细分析一下,其实并不是很难;
只要从 [click[0],click[1]]位置 进行依次 DFS 遍历,判断附近是否存在地雷、揭露相邻的空方格即可。
- 判断该位置是否是地雷或者是否已经被揭露
- 判断该位置附近是否存在地雷,地雷的个数;如果该位置与地雷相邻,将该位置修改成地雷的个数,返回即可
- 依次揭露附近的空方格
代码实现
cpp
class Solution {
int dx[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
int dy[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
int getNearM(vector<vector<char>>& board, int x, int y) {
int ret = 0;
int m = board.size(), n = board[0].size();
for (int i = 0; i < 8; i++) {
int posx = x + dx[i];
int posy = y + dy[i];
if (posx >= 0 && posx < m && posy >= 0 && posy < n &&
(board[posx][posy] == 'M' || board[posx][posy] == 'X'))
ret++;
}
return ret;
}
void dfs(vector<vector<char>>& board, int x, int y) {
if (board[x][y] == 'M') {
board[x][y] = 'X';
return;
}
int num = getNearM(board, x, y);
if (num > 0) {
board[x][y] = '0' + num;
return;
}
board[x][y] = 'B';
int m = board.size(), n = board[0].size();
for (int i = 0; i < 8; i++) {
int posx = x + dx[i];
int posy = y + dy[i];
if (posx >= 0 && posx < m && posy >= 0 && posy < n &&
board[posx][posy] == 'E')
dfs(board, posx, posy);
}
}
public:
vector<vector<char>> updateBoard(vector<vector<char>>& board,
vector<int>& click) {
dfs(board, click[0], click[1]);
return board;
}
};
七、衣橱整理
题目解析

有一个 m*n 的二维矩阵,g[i][j]表示一个需要整理的格子,现在要从 g[0][0]开始逐行逐列的整理每一个格子。
在整理过程中,可以向下或者向右移动一格;
当 digit(i) + digit(j) > cnt时,表示g[i][j]这个格子不需要整理(digit(x)表示数 x 的各位数之和)
要返回 : 总共需要整理多少个格子
算法思路
这道题虽然只给了数字 m、n以及 cnt,但构想出一个二维数组,从[0,0]位置进行一次 DFS 遍历即可。
DFS 遍历:
- 判断当前位置是否需要整理(如果不需要直接返回)
- 标记当前位置(已经遍历过),统计结果
- 遍历当前位置 右、下两个方向(没有遍历过,再进行 DFS 遍历)。
代码实现
cpp
class Solution {
int dx[2] = {0, 1};
int dy[2] = {1, 0};
int result = 0;
bool vis[110][110];
public:
int digit(int x) {
int ret = 0;
while (x) {
ret += (x % 10);
x /= 10;
}
return ret;
}
void dfs(int m, int n, int x, int y, int cnt) {
if (digit(x) + digit(y) > cnt)
return;
result++;
vis[x][y] = true;
for (int i = 0; i < 2; i++) {
int posx = x + dx[i];
int posy = y + dy[i];
if (posx < m && posy < n && !vis[posx][posy])
dfs(m, n, posx, posy, cnt);
}
}
int wardrobeFinishing(int m, int n, int cnt) {
int ret = 0;
dfs(m, n, 0, 0, cnt);
return result;
}
};
sy])
dfs(m, n, posx, posy, cnt);
}
}
int wardrobeFinishing(int m, int n, int cnt) {
int ret = 0;
dfs(m, n, 0, 0, cnt);
return result;
}
};
本篇文章主要分享三道经典DFS类题目的思路与代码实现,供刷题参考