LeetCode刷题之DFS算法
1.基本思路及代码框架
使用DFS或BFS算法遍历二维数组
在二维矩阵中使用DFS搜索,就是把二维矩阵中的每一个位置看成一个节点,这个节点的上下左右四个位置就是相邻节点,那么整个可以抽象为一幅网状的图结构。
根据数据结构和算法思维的框架,根据二叉树的遍历框架写出二维矩阵的DFS代码框架:
//二叉树的遍历框架
void traverse(TreeNode *root) {
//遍历本节点,前序遍历
traverse(roo->left);
//遍历本节点,中序遍历
traverse(roo->right);
//遍历本节点,后序遍历
}
//二维矩阵遍历框架
void dfs(vector<vector<int>> &grid, int i, int j, vector<bool> &visited) {
int m = grid.size(), n = grid[0].size();
//判断节点是否越界
if (i < 0 || j < 0 || i >= m || j >= n) {
return;
}
//判断是否访问过
if (visited[i][j]) {
return;
}
//访问节点(i,j)
visited[i][j] = true;
dfs(grid, i - 1, j);//上
dfs(grid, i + 1, j);//下
dfs(grid, i, j - 1);//左
dfs(grid, i, j + 1);//右
}
因为二维数组本质是一个图,所以使用visited布尔数组可以避免走回头路。
这⾥额外说⼀个处理⼆维数组的常⽤⼩技巧,你有时会看到使⽤「⽅向数组」来处理上下左右的遍历,和前
⽂ 图遍历框架 的代码很类似:
// ⽅向数组,分别代表上、下、左、右
int[][] dirs = new int[][]{{-1,0}, {1,0}, {0,-1}, {0,1}};
void dfs(int[][] grid, int i, int j, boolean[] visited) {
int m = grid.length, n = grid[0].length;
if (i < 0 || j < 0 || i >= m || j >= n) {
// 超出索引边界
return;
}
if (visited[i][j]) {
// 已遍历过 (i, j)
return;
}
// 进⼊节点 (i, j)
visited[i][j] = true;
// 递归遍历上下左右的节点
for (int[] d : dirs) {
int next_i = i + d[0];
int next_j = j + d[1];
dfs(grid, next_i, next_j);
}
// 离开节点 (i, j)
}
2.题目解答
1.LeetCode之200岛屿数量问题
- 解题思路
使用深度优先搜索,将每一个岛屿都淹没,避免维护visited数组。 - 代码实现
/*
* @lc app=leetcode.cn id=200 lang=cpp
*
* [200] 岛屿数量
*/
// @lc code=start
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int res = 0;
int 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] == '1') {
++res;
dfs(grid, i, j);//将与此块连接的块全部淹没
}
}
}
return res;
}
void dfs(vector<vector<char>> &grid, int i, int j) {
int m = grid.size(), n = grid[0].size();
if (i < 0 || j < 0 || i >= m || j >= n) {
return;
}
if (grid[i][j] == '0') {
return;
}
grid[i][j] = '0';
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
};
// @lc code=end
2.LeetCode之1254统计封闭岛屿的数量
- 解题思路
- 与上面思路一致,不过本题不同的是周围靠边的不算岛屿,所以先要将四周的岛屿进行淹没,然后再进行深度遍历。
- 除了使用DFS/BFS算法以外,还可以使用Union Find算法运用(后续学习在实现)。
- 代码实现
/*
* @lc app=leetcode.cn id=1254 lang=cpp
*
* [1254] 统计封闭岛屿的数目
*/
// @lc code=start
class Solution {
public:
int closedIsland(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
for (int i = 0; i < n; i++) {
dfs(grid, 0, i);
dfs(grid, m - 1, i);
}
for (int j = 0; j < m; j++) {
dfs(grid, j, 0);
dfs(grid, j, n - 1);
}
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++){
if (grid[i][j] == 0) {
++res;
dfs(grid, i, j);
}
}
}
return res;
}
void dfs(vector<vector<int>> &grid, int i, int j) {
int m = grid.size(), n = grid[0].size();
if (i < 0 || j < 0 || i >= m || j >= n) {
return;
}
if (grid[i][j] == 1) {
return;
}
grid[i][j] = 1;
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
};
// @lc code=end
3.LeetCode之1020飞地的数量
- 解题思路
与上面思路一致,将周围的岛屿淹没后,统计剩下陆地块的数量即为所求。 - 代码实现
/*
* @lc app=leetcode.cn id=1020 lang=cpp
*
* [1020] 飞地的数量
*/
// @lc code=start
class Solution {
public:
int numEnclaves(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
for (int i = 0; i < n; i++) {
dfs(grid, 0, i);
dfs(grid, m - 1, i);
}
for (int j = 0; j < m; j++) {
dfs(grid, j, 0);
dfs(grid, j, n - 1);
}
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
res += 1;
}
}
}
return res;
}
void dfs(vector<vector<int>> &grid, int i, int j) {
int m = grid.size(), n = grid[0].size();
if (i < 0 || j < 0 || i >= m || j >=n) {
return;
}
if (grid[i][j] == 0) {
return;
}
grid[i][j] = 0;
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
};
// @lc code=end
4.LeetCode之695岛屿的最大面积
- 解题思路
遍历岛屿,计算每个岛屿的面积,返回最大的面积。 - 代码实现
/*
* @lc app=leetcode.cn id=695 lang=cpp
*
* [695] 岛屿的最大面积
*/
// @lc code=start
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
int maxSize = 0;
for (int i = 0; i < m; i++) {
for (int j = 0 ; j < n; j++) {
int s = 0;
if (grid[i][j] == 1) {
dfs(grid, i, j, s);
maxSize = max(maxSize, s);
}
}
}
return maxSize;
}
void dfs(vector<vector<int>> &grid, int i, int j, int &s) {
int m = grid.size(), n = grid[0].size();
if (i < 0 || j < 0 || i >= m || j >=n) {
return;
}
if (grid[i][j] == 0) {
return;
}
grid[i][j] = 0;
s = s + 1;
dfs(grid, i - 1, j, s);
dfs(grid, i + 1, j, s);
dfs(grid, i, j - 1, s);
dfs(grid, i, j + 1, s);
}
};
// @lc code=end
5.LeetCode之1905统计子岛屿
- 解题思路
如果岛屿2中有的陆地而岛屿1中对应的位置为水,说明这个陆地所处的岛屿肯定不是子岛屿,可直接将其淹没。剩下的岛屿都为子岛屿。 - 代码实现
/*
* @lc app=leetcode.cn id=1905 lang=cpp
*
* [1905] 统计子岛屿
*/
// @lc code=start
class Solution {
public:
int countSubIslands(vector<vector<int>>& grid1, vector<vector<int>>& grid2) {
int m = grid2.size(), n = grid2[0].size();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid1[i][j] == 0 && grid2[i][j] == 1) {
dfs(grid2, i ,j);
}
}
}
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid2[i][j] == 1) {
++res;
dfs(grid2, i, j);
}
}
}
return res;
}
void dfs(vector<vector<int>> &grid, int i, int j) {
int m = grid.size(), n = grid[0].size();
if (i < 0 || j < 0 || i >= m || j >= n) {
return;
}
if (grid[i][j] == 0) {
return;
}
grid[i][j] = 0;
dfs(grid, i - 1, j);
dfs(grid, i + 1, j);
dfs(grid, i, j - 1);
dfs(grid, i, j + 1);
}
};
// @lc code=end
6.LeetCode之694不同的岛屿数量
- 看完图的遍历和二叉树的序列化和反序列化再来写。