力扣HOT100刷题记录-图论-题解-200.岛屿数量

4 阅读4分钟

岛屿数量(Number of Islands)详细解题笔记

1. 题目描述

题目名称:岛屿数量

题目描述:给定一个由 '1'(陆地)和 '0'(水)组成的二维网格,计算岛屿的数量。岛屿被水包围,并且通过水平或垂直方向上相邻的陆地连接而成。

输入

`grid = [

['1','1','1','1','0'],

['1','1','0','1','0'],

['1','1','0','0','0'],

['0','0','0','0','0']

]`

输出1

解释:所有陆地都相连,形成一个岛屿。

约束条件

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j]的值为 '0' 或 '1'

2. 解题思路分析

方法一:深度优先搜索(DFS)推荐

思路:遍历每个网格点,当发现陆地('1')时,使用DFS标记所有相连的陆地为已访问(沉没岛屿),岛屿计数+1。

时间复杂度:O(m × n) - 每个网格点最多访问一次

空间复杂度:O(m × n) - 最坏情况下递归深度

#include<iostream>
#include<vector>

using namespace std;

void dfs(vector<vector<char>>& graph,int r,int c){//当前行,当前列
    int nr=graph.size();
    int nc=graph[0].size();

    graph[r][c]='0';//标记已经访问

    //检查上下左右
    if(r>0&&graph[r-1][c]=='1'){
        dfs(graph,r-1,c);
    }

    if(r<nr-1&&graph[r+1][c]=='1'){
        dfs(graph,r+1,c);
    }

    if(c>0&&graph[r][c-1]=='1'){
        dfs(graph,r,c-1);
    }

    if(c<nc-1&&graph[r][c+1]=='1'){
        dfs(graph,r,c+1);
    }
    
}

int main(){
    vector<vector<char>>graph={{'1','1','1','1','0'},{'1','1','0','1','0'},{'1','1','0','0','0'},{'0','0','0','0','0'}};
    int result=0;
    for(int i=0;i<graph.size();i++){
        for(int j=0;j<graph[0].size();j++){
            if(graph[i][j]=='1'){
                dfs(graph,i,j);
                result++;
            }
        }
    }
    cout<<result<<endl;

  
}

方法二:广度优先搜索(BFS)

思路:使用队列代替递归,实现层序遍历。

时间复杂度:O(m × n)

空间复杂度:O(min(m, n)) - 队列最大长度

#include<iostream>
#include<vector>
#include<queue>

using namespace std;

void bfs(vector<vector<char>>& graph,int r,int c){//当前行,当前列
    int nr=graph.size();
    int nc=graph[0].size();

    queue<pair<int,int>>q;//队列存储当前访问的节点
    q.push({r,c});//将当前节点入队
    graph[r][c]='0';//标记已经访问

    while(!q.empty()){
        auto node=q.front();//取出队头节点
        q.pop();//将队头节点出队
        int row=node.first;//当前行
        int col=node.second;//当前列

        //检查上下左右
        if(row>0&&graph[row-1][col]=='1'){
            q.push({row-1,col});
            graph[row-1][col]='0';
        }

        if(row<nr-1&&graph[row+1][col]=='1'){
            q.push({row+1,col});
            graph[row+1][col]='0';
        }

        if(col>0&&graph[row][col-1]=='1'){
            q.push({row,col-1});
            graph[row][col-1]='0';
        }

        if(col<nc-1&&graph[row][col+1]=='1'){
            q.push({row,col+1});
            graph[row][col+1]='0';
        }
    }

}





int main(){
    vector<vector<char>>graph={{'1','1','1','1','0'},{'1','1','0','1','0'},{'1','1','0','0','0'},{'0','0','0','0','0'}};
    int result=0;

    for(int i=0;i<graph.size();i++){
        for(int j=0;j<graph[0].size();j++){
            if(graph[i][j]=='1'){
                bfs(graph,i,j);
                result++;
            }
        }
    }

    cout<<result<<endl;

  
}

3. 核心思想与关键问题解答

Q1: 为什么选择DFS/BFS?

A: 岛屿问题本质是连通分量问题,DFS/BFS是解决连通性问题的标准方法,能高效标记所有相连节点。

Q2: 为什么使用原地修改(沉没岛屿)?

A: 将访问过的陆地标记为'0',既避免了重复访问,又节省了O(m×n)的额外空间,是空间最优解。

Q3: 该算法的适用场景是什么?

A: 适用于所有基于网格的连通性问题,如"被围绕的区域"、"最大岛屿面积"、"岛屿周长"等。

Q4: 如何扩展到同类问题?

A: 对于"统计岛屿最大面积",在DFS中增加计数器;对于"岛屿周长",统计陆地与水的边界。

4. 算法流程详解

以输入示例为例:

步骤当前坐标当前值操作岛屿计数说明
1(0,0)'1'发现新岛屿,DFS沉没1开始DFS
2(0,1)'1'标记'0',继续DFS1向右扩散
3(0,2)'1'标记'0',继续DFS1向右扩散
4(0,3)'1'标记'0',继续DFS1向右扩散
5(0,4)'0'停止向右1遇到水
6(1,0)'1'标记'0',向下扩散1向下扩散
............1继续沉没所有相连陆地
结束---1所有陆地已处理

最终输出:1

5. 总结 / 适用场景扩展

核心技巧

  • 使用DFS/BFS标记连通分量
  • 原地修改节省空间复杂度
  • 边界检查防止数组越界

同类题型扩展

  1. 最大岛屿面积:在DFS中统计连通块大小
  2. 被围绕的区域:从边界开始DFS标记不被包围的区域
  3. 岛屿周长:统计陆地与水的相邻边数
  4. 统计封闭岛屿:排除边界连接的岛屿

6. 边界情况 / 测试用例 / 变体思考

边界测试用例

  1. 全水网格[['0','0'],['0','0']]→ 返回0
  2. 全陆网格[['1','1'],['1','1']]→ 返回1
  3. 单点岛屿[['1','0'],['0','0']]→ 返回1
  4. 无连接岛屿[['1','0'],['0','1']]→ 返回2