LeetCode刷题之DFS算法
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不同的岛屿数量
- 看完图的遍历和二叉树的序列化和反序列化再来写。