Leetcode 200 邻接岛屿
看到题解总结的不错,给出了岛屿问题的主要变形和基本模板。总结如下:
过程:本质是对DFS的理解
- 二叉树的dfs两个要素:标记相邻节点和判断base case
第一个要素是访问相邻节点。二叉树的相邻节点非常简单,只有左子结点和右子结点两个。二叉树本身就是一个递归定义的结构:一棵二叉树,它的左子树和右子树也是一棵二叉树。那么我们的 DFS 遍历只需要递归调用左子树和右子树即可。
第二个要素是 判断 base case。一般来说,二叉树遍历的 base case 是 root == null。这样一个条件判断其实有两个含义:一方面,这表示 root 指向的子树为空,不需要再往下遍历了。另一方面,在 root == null 的时候及时返回,可以让后面的 root.left 和 root.right 操作不会出现空指针异常。
岛屿问题通解:
void dfs(int[][] grid, int r, int c) {
// 判断 base case
if (!inArea(grid, r, c)) {
return;
}
// 如果这个格子不是岛屿,直接返回
if (grid[r][c] != 1) {
return;
}
grid[r][c] = 2; // 将格子标记为「已遍历过」
// 访问上、下、左、右四个相邻结点
dfs(grid, r - 1, c);
dfs(grid, r + 1, c);
dfs(grid, r, c - 1);
dfs(grid, r, c + 1);
}
// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int r, int c) {
return 0 <= r && r < grid.length && 0 <= c && c < grid[0].length;
}
Python实现
def numIslands(self, grid: List[List[str]]) -> int:
res = 0
if not grid:return 0
def dfs(i,j):
if i<0 or i>=len(grid) or j<0 or j>=len(grid[0]) or grid[i][j] != "1":
return
grid[i][j] = "0"
dfs(i-1,j)
dfs(i+1,j)
dfs(i,j-1)
dfs(i,j+1)
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == "1":
dfs(i,j)
res += 1
return res
C++实现 (注意.size()用法)
public:
void dfs(vector<vector<char>>& grid, int r, int c){
//base case
if(!inArea(grid, r, c)){
return;
}
//如果非岛屿,直接返回
if (grid[r][c] != '1'){
return;
}
grid[r][c] = '2';//标记已遍历格子
//访问节点
dfs(grid, r - 1, c);
dfs(grid, r + 1, c);
dfs(grid, r, c - 1);
dfs(grid, r, c + 1);
}
bool inArea(vector<vector<char>>& grid, int r, int c){
return 0 <= r && r < grid.size() && 0 <= c && c < grid[0].size();
}
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'){
dfs(grid,i,j);
res++;
}
}
}
return res;
}
};
相关例题:
Leetcode 695岛屿最大面积 ( area计数型+ int标识)
C++实现:
public:
int area(vector<vector<int>>& grid, int r, int c){
//base case
if(!inArea(grid, r, c)){
return 0;
}
//如果非岛屿,直接返回
if (grid[r][c] != 1){
return 0;
}
grid[r][c] = 2;//标记已遍历格子
//访问节点
return 1
+ area(grid, r - 1, c)
+ area(grid, r + 1, c)
+ area(grid, r, c - 1)
+ area(grid, r, c + 1);
}
bool inArea(vector<vector<int>>& grid, int r, int c){
return 0 <= r && r < grid.size() && 0 <= c && c < grid[0].size();
}
int maxAreaOfIsland(vector<vector<int>>& 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){
int a = area(grid,i,j);
res = std::max(res, a);
}
}
}
return res;
}
};
Leetcode 463 岛屿周长(边界对应dfs边界, 黄边+蓝边)
C++版本:
public:
int islandPerimeter(vector<vector<int>>& grid) {
for (int r = 0; r < grid.size(); r++) {
for (int c = 0; c < grid[0].size(); c++) {
if (grid[r][c] == 1) {
// 题目限制只有一个岛屿,计算一个即可
return dfs(grid, r, c);
}
}
}
return 0;
}
int dfs(vector<vector<int>>& grid, int r, int c) {
// 函数因为「坐标 (r, c) 超出网格范围」返回,对应一条黄色的边
if (!inArea(grid, r, c)) {
return 1;
}
// 函数因为「当前格子是海洋格子」返回,对应一条蓝色的边
if (grid[r][c] == 0) {
return 1;
}
// 函数因为「当前格子是已遍历的陆地格子」返回,和周长没关系
if (grid[r][c] != 1) {
return 0;
}
grid[r][c] = 2;
return dfs(grid, r - 1, c)
+ dfs(grid, r + 1, c)
+ dfs(grid, r, c - 1)
+ dfs(grid, r, c + 1);
}
// 判断坐标 (r, c) 是否在网格中
bool inArea(vector<vector<int>>& grid, int r, int c) {
return 0 <= r && r < grid.size() && 0 <= c && c < grid[0].size();
}
};