DFS(695. 岛屿的最大面积、200. 岛屿数量、547. 省份数量、130. 被围绕的区域、417. 太平洋大西洋水流问题)

目录

一、695. 岛屿的最大面积

1.1 题目描述

1.2 代码

1.2.1 DFS

二、200. 岛屿数量

2.1 题目描述

2.2 代码

2.2.1 DFS

三、547. 省份数量

3.1 题目描述

3.2 代码 

3.2.1 DFS

四、130. 被围绕的区域

4.1 题目描述

4.2 代码

4.2.1 DFS

五、417. 太平洋大西洋水流问题

5.1 题目描述

5.2 代码

5.2.1 DFS


一、695. 岛屿的最大面积

1.1 题目描述

给定一个包含了一些 0 和 1 的非空二维数组 grid 。(难度中等)

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

1.2 代码

1.2.1 DFS

算法思路:力扣

我们想知道网格中每个连通形状的面积,然后取最大值。

如果我们在一个土地上,以 44个方向探索与之相连的每一个土地(以及与这些土地相连的土地),那么探索过的土地总数将是该连通形状的面积。

为了确保每个土地访问不超过一次,我们每次经过一块土地时,将这块土地的值置为 0。这样我们就不会多次访问同一土地。

class Solution {
private int m,n;
private int[][] direction={{1,0},{-1,0},{0,-1},{0,1}};//上下左右四个方向

    public  int maxAreaOfIsland(int[][] grid) {
         m = grid.length;
         n= grid[0].length;
          if (m==0 || n==0 || grid==null){
          return 0;
      }
      int maxArea=0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
               maxArea = Math.max(maxArea,dfs(grid,i,j));
            }
        }
        return maxArea;
    }

    public  int  dfs(int[][]grid,int row,int column){
        if (row<0 ||row>=m ||column <0 ||column>=n|| grid[row][column] == 0){
            return 0;
        }
        grid[row][column]=0;
        int area=1;
        for (int[] d:direction){
            area+=dfs(grid,row+d[0],column+d[1]);
        }
        return area;
}
}

复杂度分析

时间复杂度:O(R×C)。其中 R 是给定网格中的行数,C 是列数。我们访问每个网格最多一次。

空间复杂度:O(R×C),递归的深度最大可能是整个网格的大小,因此最大可能使用O(R×C) 的栈空间。

注意不要把curCount当做变量传递,因为curCount值不能被传递,需要写为全局变量。 

class Solution695 {
    //注意不要把curCount当做变量传递,因为curCount值不能被传递,需要写为全局变量
    static int  max,row,col,curCount;
    static int[][] direction={{0,1},{1,0},{0,-1},{-1,0}};
    public static int maxAreaOfIsland(int[][] grid) {
        row=grid.length;
        col=grid[0].length;
        curCount=0;
        max=0;
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j]==1){
                    curCount=0;
                    backing(grid,i,j);
                    //max=Math.max(max,curCount);//此处curCount局部变量和全局变量时返回值都为0,函数里面才有准确的值
                }
            }
        }
        return max;
    }

    private static void backing(int[][] grid, int index1, int index2) {
        if (index1<0 || index1>=row || index2<0 || index2>=col || grid[index1][index2]==0 ) return;
        curCount++;
        grid[index1][index2]=0;
        for (int[] dir:direction){
            backing(grid,index1+dir[0],index2+dir[1]);
        }
        max=Math.max(max,curCount);
    }

}

 

二、200. 岛屿数量

2.1 题目描述

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。(难度中等)

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。此外,你可以假设该网格的四条边均被水包围。

2.2 代码

2.2.1 DFS

做法与上题大致相同,不同的是,此题遇到‘1’时,深度遍历此‘1’所有的连通的陆地,将其都置为0。则下一次遇到‘1’时,则是新的岛屿,islanNum++。

 private int[][] direction={{1,0},{-1,0},{0,-1},{0,1}};//上下左右四个方向
    private int m,n;
    public int numIslands(char[][] grid) {
        m = grid.length;
        n= grid[0].length;
        if (m==0 || n==0 || grid==null){
            return 0;
        }
        int islandNum=0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j]=='1'){
                    dfs(grid,i,j);
                islandNum++;
                }
            }
            }
return islandNum;
    }

    public void dfs(char[][] grid,int row,int column){
        if (row<0 ||row>=m ||column <0 ||column>=n|| grid[row][column] == '0'){
           return;
        }
        grid[row][column]='0';
        for (int[] d :direction){
            dfs(grid,row+d[0],column+d[1]);

        }

    }

复杂度分析:

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

三、547. 省份数量

3.1 题目描述

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。(难度中等)

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中省份的数量。

       

3.2 代码 

3.2.1 DFS

思路:力扣

可以把 n个城市和它们之间的相连关系看成图,城市是图中的节点,相连关系是图中的边,给定的矩阵isConnected 即为图的邻接矩阵,省份即为图中的连通分量。计算省份总数,等价于计算图中的连通分量数,可以通过深度优先搜索或广度优先搜索实现,也可以通过并查集实现。

  • 要判断i和谁相连,则只需要看isConnected数组的第i行,若isConnected[i][m]==1,则说明i与m直接相连。因此看i与谁连通,只需要看第i行。
  • 遍历城市i,将与i相连的城市都标记为访问,下一次遇到未访问的城市,则是新的省份。
 int provinces;
    public int findCircleNum(int[][] isConnected) {
        provinces = isConnected.length;
        int circle=0;
        boolean[] isVisited =new  boolean[provinces];
        for (int i=0;i<provinces;i++){
            //若i城市未访问过,则深度遍历i
            if (!isVisited[i]){
                circle++;
                dfs(isConnected,isVisited,i);
            }
        }
        return circle;
    }
    public void dfs(int[][] isConnected, boolean[] isVisited,int i){
        for (int j = 0; j < provinces; j++) {
            //i与j连通,且j未访问过,则深度遍历j城市,将与j相连通的城市都访问了
            if (!isVisited[j] && isConnected[i][j]==1){
                isVisited[j]=true;
                dfs(isConnected,isVisited,j);//i与j连通,现在看j与谁连通,访问
            }
        }

    }

四、130. 被围绕的区域

4.1 题目描述

给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。(难度中等)

4.2 代码

4.2.1 DFS

思路:力扣

先处理边界,使用dfs,将与边界‘0’相连的‘0’都标记为‘#’,则剩余的‘O’,即为被‘X’包围的点。最后我们遍历board将为‘#’的修改为‘O’,将‘O’修改为‘X’.

    public int m,n;
    public int[][] direction ={{1,0},{-1,0},{0,1},{0,-1}} ;
    public void solve(char[][] board) {
        m = board.length;
        n = board[0].length;
        if (m == 0 || n == 0 || board == null)
            return;

        //处理边界
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                boolean isEdge = (i == 0 || i == m - 1 || j == 0 || j == n - 1);
                if (isEdge && board[i][j] == 'O') {
                    dfs(board, i, j);
                }
            }
        }
        for(int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j]=='O'){
                    board[i][j]='X';
                } else if (board[i][j]=='#')
                    board[i][j]='O';
            }
        }
    }


        //使用dfs将与边界‘O’相连的‘O’都设置为‘#’
        public void dfs(char[][] board,int row,int column){
            if (row<0 || row>=m||column<0 || column>=n||board[row][column]!='O'){
                return;
            }
            board[row][column]='#';
            for (int d[]:direction){
                dfs(board,row+d[0],column+d[1]);
            }
    }

五、417. 太平洋大西洋水流问题

5.1 题目描述

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。(难度中等)

规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。

请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

提示:输出坐标的顺序不重要,m 和 n 都小于150

5.2 代码

5.2.1 DFS

此题与130相似,先处理边界。

思路:如果我们从每一个地方dfs,那么时间复杂度就会很大。因此我们可以反过来想从大洋往上流。这样对于两个大洋,我们从其对应的边界开始遍历,将从大洋可以到达的点标记。完成后,只需遍历一下标记矩阵,满足条件的位置就是两个大洋都能到达的位置。

标记矩阵:canReach[r][c]  

dfs,刚开始调用,标记的都是边界,之后满足条件的才调用dfs,因此标记矩阵标记的都是大洋可达的点。

/**
 * 如果到达了左和上代表到了太平洋i=0||j=0
 * 如果到了右和下说明可以到大西洋i=m-1||j=n-1
 **/
 private int m, n;
    private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    public List<List<Integer>> pacificAtlantic(int[][] heights) {
        //List<List<Integer>> ret = new ArrayList<>();
        List<List<Integer>> ret =new LinkedList<>();//两种列表均可
        m = heights.length;
        n = heights[0].length;
        if (heights == null || m == 0) {
            return ret;
        }
        boolean[][] canReachP = new boolean[m][n];
        boolean[][] canReachA = new boolean[m][n];

        //从大洋的边开始遍历
        for (int i = 0; i < m; i++) {
            dfs(i, 0, canReachP,heights);//左侧
            dfs(i, n - 1, canReachA,heights);//右侧
        }
        for (int i = 0; i < n; i++) {
            dfs(0, i, canReachP,heights);//上侧
            dfs(m - 1, i, canReachA,heights);//下侧
        }

        效率不高,不能用else if,因为存在[m-1][0] 左下角,和两河都相连
//        for (int i = 0; i < m; i++) {
//            for (int j = 0; j < n; j++) {
//                // 从边缘o开始搜索
//                boolean isEdgeA = (i == m-1 || j == n-1 );
//                boolean isEdgeP = (i == 0 || j == 0);
//                if (isEdgeA) {
//                    dfs(i, j,canReachA,heights);//访问与边界相连的O,全部标记为T
//                }
//                if (isEdgeP) {
//                    dfs(i, j,canReachP,heights);//访问与边界相连的O,全部标记为T
//                }
//            }
//        }

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (canReachP[i][j] && canReachA[i][j]) {//canReachA[i][j]记录的是[i][j]能否到达A
                    ret.add(Arrays.asList(i, j));//Arrays.asList,该方法是将数组转化为list
                    //列表的第一个元素是i,第二个是j
                }
            }
        }
        return ret;
    }
//从大洋往上流,将可以到达的点标记为可达
    private void dfs(int r, int c, boolean[][] canReach,int[][] heights) {
        if (canReach[r][c]) {
            return;
        }
        canReach[r][c] = true;//为了不重复搜索
        for (int[] d : direction) {
            int nextR = d[0] + r;
            int nextC = d[1] + c;
            if (nextR >= 0 && nextR < m && nextC >= 0 && nextC < n
                    && heights[r][c] <=heights[nextR][nextC]) {
                //因为是倒流,因此下次访问的高度要大于现在高度的
                dfs(nextR, nextC, canReach,heights);
            }
        }
    }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值