【面试题】岛屿数量(DFS+BFS)

题目描述

【面试题】岛屿数量(DFS+BFS)_第1张图片

解法一:DFS

染色法,扫描整个二维网格。如果一个位置为 1,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。最终岛屿的数量就是我们进行深度优先搜索的次数。

class Solution {
    public int numIslands(char[][] grid) {
        if(grid.length == 0 || grid[0].length == 0) return 0;
        int rows = grid.length;
        int cols = grid[0].length;
        int nums = 0;
        for(int i=0; i<rows; i++) {
            for(int j=0; j<cols; j++) {
                // 如果一个位置为 1,则以其为起始节点开始进行深度优先搜索,其周围每个搜索到的 1 都会被重新标记为 0
                if(grid[i][j] == '1') { 
                    dfs(grid, i, j);
                    nums++;
                }
            }
        }
        return nums;
    }

    public void dfs(char[][] grid, int i, int j) {
        // 如果索引越界,或者当前格子上为‘0’,则直接跳过
        if(i<0 || i>= grid.length || j<0 || j>= grid[0].length || 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); // 向右递归搜索
    }
}

复杂度分析:

  • 时间复杂度:O(MN),其中 M 和 N 分别为行数和列数。
  • 空间复杂度:O(MN),在最坏情况下,整个网格均为陆地,深度优先搜索的深度达到 M *M。

解法二:BFS

扫描整个二维网格。如果一个位置为 1,则将其加入队列,开始进行广度优先搜索。在广度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。直到队列为空,搜索结束。最终岛屿的数量就是我们进行广度优先搜索的次数。

在写 “广度优先遍历” 的时候,要注意一点:
所有加入队列的结点,都应该马上(在入队的时候而不是出队的时候)被标记为 “已经访问”,反正只要进入了队列,你迟早都会遍历到它,否则,如果是等到出队列的时候再标记,会造成很多重复的结点进入队列,造成重复的操作

class Solution {
    public int numIslands(char[][] grid) {
        if (grid.length == 0 || grid[0].length == 0) return 0;
        int rows = grid.length, cols = grid[0].length;
        boolean[][] visited = new boolean[rows][cols];
        int count = 0;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                // 如果某个点为陆地,并且没有被访问过, 则从该点开始进行广度优先遍历
                if (!visited[i][j] && grid[i][j] == '1') {
                    count++;
                    Queue<Integer> q = new LinkedList<> ();
                    // 小技巧:把二维坐标转换为一个数字,否则,得用一个数组存
                    q.offer(i * cols + j);
                    // 注意:在入队的时候就马上要标记该点已经访问过
                    visited[i][j] = true;
                    while (!q.isEmpty()) {
                        int cur = q.poll(); // 如果队列不为空,则队首元素出队
                        // 从队列中存放的数字转换回矩阵中的坐标
                        int curX = cur / cols;
                        int curY = cur % cols;
                        // 将上方的陆地节点入队
                        if(curX-1 >= 0 && grid[curX-1][curY] == '1' && !visited[curX-1][curY]){ // 判断索引是否越界,是否为陆地,是否已访问过
                            q.offer((curX-1)*cols + curY);
                            visited[curX-1][curY] = true; // 放入队列以后,要马上标记成已经访问过
                        }
                        // 将下方的陆地节点入队
                        if(curX+1 < rows && grid[curX+1][curY] == '1' && !visited[curX+1][curY]){
                            q.offer((curX+1)*cols + curY);
                            visited[curX+1][curY] = true;
                        }
                        // 将左方的陆地节点入队
                        if(curY-1 >= 0 && grid[curX][curY-1] == '1' && !visited[curX][curY-1]){
                            q.offer(curX*cols + (curY-1));
                            visited[curX][curY-1] = true;
                        }
                        // 将右方的陆地节点入队
                        if(curY+1 < cols && grid[curX][curY+1] == '1' && !visited[curX][curY+1]){
                            q.offer(curX*cols + (curY+1));
                            visited[curX][curY+1] = true;
                        }                   
                    }
                }
            }
        }
        return count;
    }
}

另一种写法

class Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        for(int i = 0; i < grid.length; i++) {
            for(int j = 0; j < grid[0].length; j++) {
                if(grid[i][j] == '1'){
                    bfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }
    private void bfs(char[][] grid, int i, int j){
        Queue<int[]> list = new LinkedList<>(); // 队列中存放的是二维坐标(x,y)
        list.add(new int[] { i, j }); 
        while(!list.isEmpty()){
            int[] cur = list.remove();
            i = cur[0]; j = cur[1];
            if(0 <= i && i < grid.length && 0 <= j && j < grid[0].length && grid[i][j] == '1') {
                grid[i][j] = '0'; // 已经访问过的陆地节点就置为 0
                list.add(new int[] { i + 1, j });
                list.add(new int[] { i - 1, j });
                list.add(new int[] { i, j + 1 });
                list.add(new int[] { i, j - 1 });
            }
        }
    }
}

复杂度分析:

  • 时间复杂度:O(MN),其中 M 和 N 分别为行数和列数。
  • 空间复杂度:O(min(M,N)),在最坏情况下,整个网格均为陆地,队列的大小达到min(M, N),因为bfs在二维矩阵中是斜着扫描的,当矩阵全为 1时,从左上向右下遍历,最长路径为对角线上的格子数目。

参考

https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-python-dai-ma-java-dai-ma-by-l/

https://leetcode-cn.com/problems/number-of-islands/solution/number-of-islands-shen-du-you-xian-bian-li-dfs-or-/

https://leetcode-cn.com/problems/number-of-islands/solution/dao-yu-shu-liang-by-leetcode/

你可能感兴趣的:(剑指offer)