通过求岛屿的数量,学习深度优先搜索和广度优先搜索

问题描述

给定一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。(来源:力扣(LeetCode))。多个陆地连接在一起,算作一个岛屿,单个陆地在一起也算一个岛屿。
示例 1:
输入:
11110
11010
11000
00000
输出: 1

示例 2:
输入:
11000
11000
00100
00011
输出: 3

广度优先搜索(BFS) 解决问题

广度优先搜索(也称宽度优先搜索,缩写BFS,以下采用广度来描述)是连通图的一种遍历算法这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。基本过程,BFS是从根节点开始,沿着树(图)的宽度遍历树(图)的节点。如果所有节点均被访问,则算法中止。一般用队列数据结构来辅助实现BFS算法。

伪代码
void  BFS(){
    队列初始化;
    初始结点入队;
    while (队列非空){  
          队头元素出队,赋给current;
          while(current 还可以扩展){
              由结点current扩展出新结点newifnew 重复于已有的结点状态) continue;
              new结点入队;
              if  (new结点是目标状态){
                    置flag= true;    break; 
               }
          }
      }
}
解题

线性扫描整个二维网格,如果一个结点包含 1,则以其为根结点启动广度优先搜索。将其放入队列中,并将值设为 0 以标记访问过该结点。迭代地搜索队列中的每个结点,直到队列为空。

  public static int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int count = 0; //岛屿的数量
        int Y = grid.length; //水域的高度
        int X = grid[0].length; //水域的宽度

        //遍历每个区域 x y 代表其坐标。
        for (int y = 0; y < Y; y++) {
            for (int x = 0; x < X; x++) {
                //找到陆地后 “炸了”, 岛屿数量+1,广度搜索旁边陆地,也“炸了”
                if (grid[y][x] == '1') {
                    grid[y][x] = '0';
                    count++;
                    Queue<Integer> queue = new LinkedList<>();
                    // y * X + x 表示每个坐标的唯一标号
                    queue.add(y * X + x);
                    while (!queue.isEmpty()) {
                        //将头部的岛屿移除,并返回他的编号
                        int number = queue.remove();
                        //根据编号计算坐标
                        int x1 = number % X;
                        int y1 = number / X;
                        //把该坐标周围的陆地都找出来 并炸了
                        if (y1 - 1 >= 0 && grid[y1 - 1][x1] == '1') {
                            queue.add((y1 - 1) * X + x1);
                            grid[y1 - 1][x1] = '0';
                        }
                        if (y1 + 1 < Y && grid[y1 + 1][x1] == '1') {
                            queue.add((y1 + 1) * X + x1);
                            grid[y1 + 1][x1] = '0';
                        }
                        if (x1 - 1 >= 0 && grid[y1][x1 - 1] == '1') {
                            queue.add(y1 * X + (x1 - 1));
                            grid[y1][x1 - 1] = '0';
                        }
                        if (x1 + 1 < X && grid[y1][x1 + 1] == '1') {
                            queue.add(y1 * X + x1 + 1);
                            grid[y1][x1 + 1] = '0';
                        }
                    }
                }
            }
        }
        return count;
    }

复杂度分析

  • 时间复杂度 : O(M \times N)O(M×N),其中 MM 和 NN 分别为行数和列数。
  • 空间复杂度 : O(min(M, N))O(min(M,N)),在最坏的情况下(全部为陆地),队列的大小可以达到 min(M,NM,N)。

深度优先搜索(DFS)解决问题

深度优先搜索属于图算法的一种,是一个针对图和树的遍历算法,英文缩写为DFS即Depth First Search。深度优先搜索是图论中的经典算法,利用深度优先搜索算法可以产生目标图的相应拓扑排序表,利用拓扑排序表可以方便的解决很多相关的图论问题,如最大路径问题等等。一般用堆数据结构来辅助实现DFS算法。其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。

伪代码
 void  DFS(结点类型 current){  // 从结点current出发递归地深度优先搜索
       置visited[current]=true// 表示结点current已被处理
       if  (current结点是目标状态{ 
            置搜索成功标志flag= false;
            return ;
        }
        while(current 还可以扩展){
             由current结点扩展出新结点newif! visited[new]) DFS(new); // 对未处理的结点new递归调用DFS
         }
        置visited[current]=flase;   // 表示结点current以后可能被处理
 }
解题

线性扫描整个二维网格,如果一个结点包含 1,则以其为根结点启动深度优先搜索。在深度优先搜索过程中,每个访问过的结点被标记为 0。计数启动深度优先搜索的根结点的数量,即为岛屿的数量。

public static void dfs(char[][] grid, int x, int y) {
        if(x < 0 || y < 0 || y >= grid.length || x >= grid[0].length || grid[y][x] == '0' ){
            return;
        }
        grid[y][x] = '0';
        dfs(grid, x-1, y);
        dfs(grid, x+1, y);
        dfs(grid, x, y-1);
        dfs(grid, x, y+1);
    }

    public static int numIslands2(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int count = 0; //岛屿的数量
        int Y = grid.length; //水域的高度
        int X = grid[0].length; //水域的宽度

        //遍历每个区域 x y 代表其坐标。
        for (int y = 0; y < Y; y++) {
            for (int x = 0; x < X; x++) {
                if (grid[y][x] == '1') {
                    count++;
                    dfs(grid, x, y);
                }
            }
        }
        return count;
    }

复杂度分析

  • 时间复杂度 : O(M\times N)O(M×N),其中 MM 和 NN 分别为行数和列数。
  • 空间复杂度 : 最坏情况下为 O(M \times N)O(M×N),此时整个网格均为陆地,深度优先搜索的深度达到 M \times NM×N。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值