zoukankan      html  css  js  c++  java
  • 【LeetCode】DFS 总结

    DFS(深度优先搜索) 常用来解决可达性的问题。

    两个要点:

    • 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
    • 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。

    695. 岛屿的最大面积

    题目描述

    给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。

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

    示例 1:

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

    对于上面这个给定矩阵应返回 6。注意答案不应该是11,因为岛屿只能包含水平或垂直的四个方向的‘1’。

    示例 2:

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

    对于上面这个给定的矩阵, 返回 0

    注意: 给定的矩阵grid 的长度和宽度都不超过 50。


    解题思路

    使用 DFS 遍历时返回当前岛屿的面积,便利数组时每次比较当前岛屿面积与最大岛屿面积的大小。

    private int m, n;
    private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    public int maxAreaOfIsland (int[][] grid) {
        m = grid.length;
        if (m == 0) return 0;
        n = grid[0].length;
        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;
    }
    
    private int dfs (int[][] grid, int i, int j) {
        if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == 0) {
            return 0;
        }
        grid[i][j] = 0;
        int area = 1;
        for (int[] d : direction) {
            area += dfs(grid, i + d[0], j + d[1]);
        }
        return area;
    }
    

    200. 岛屿的个数

    题目描述

    给定一个由 '1'(陆地)和 '0'(水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围。

    示例 1:

    输入:
    11110
    11010
    11000
    00000
    
    输出: 1
    

    示例 2:

    输入:
    11000
    11000
    00100
    00011
    
    输出: 3
    

    解题思路

    每次 DFS 完一个岛屿(连通分量)后,计数器+1.

    private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    
    public int numIslands (char[][] grid) {
        int m = grid.length;
        if (m == 0) return 0;
        int n = grid[0].length;
        int count = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '1') {
                    dfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }
    
    private void dfs (char[][] grid, int i, int j) {
        if (i >= 0 && j >= 0 && i < grid.length && j < grid[0].length && grid[i][j] == '1') {
            grid[i][j] = '0';
            for (int[] d : direction) {
                dfs(grid, i + d[0], j + d[1]);
            }
        }
    }
    

    547. 朋友圈

    题目描述

    1. 班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

      给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

      示例 1:

      输入: 
      [[1,1,0],
       [1,1,0],
       [0,0,1]]
      输出: 2 
      说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
      第2个学生自己在一个朋友圈。所以返回2。
      

      示例 2:

      输入: 
      [[1,1,0],
       [1,1,1],
       [0,1,1]]
      输出: 1
      说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。
      

      注意:

      1. N 在[1,200]的范围内。
      2. 对于所有学生,有M[i][i] = 1
      3. 如果有M[i][j] = 1,则有M[j][i] = 1

    解题思路

    这一题虽然也是一个二维矩阵,看似与前两个岛屿问题类似,但是本题的矩阵具有对称性,所以for循环被拆成了两部分,一部分遍历每个学生,第二部分对每个学生 DFS。

    private int n;
    public int findCircleNum (int[][] M) {
        n = M.length;
        boolean[] marked = new boolean[n];
        int circleCount = 0;
        for (int i = 0; i < n; i++) {
            if (!marked[i]) {
                dfs(M, i, marked);
                circleCount++;
            }
        }
        return circleCount;
    }
    
    private void dfs (int[][] M, int i, boolean[] marked) {
        marked[i] = true;
        for (int j = 0; j < n; j++) {
            if (M[i][j] == 1 && !marked[j]) {
                dfs(M, j, marked);
            }
        }
    }
    

    130. 被围绕的区域

    题目描述

    给定一个二维的矩阵,包含 'X''O'字母 O)。

    找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O''X' 填充。

    示例:

    X X X X
    X O O X
    X X O X
    X O X X
    

    运行你的函数后,矩阵变为:

    X X X X
    X X X X
    X X X X
    X O X X
    

    解释:

    被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O' 都不会被填充为 'X'。 任何不在边界上,或不与边界上的 'O' 相连的 'O' 最终都会被填充为 'X'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。


    解题思路

    因为所有跟边界上的‘O’连通的区域都不会被包围,所以我们通过 DFS 先找到所有跟边界连通的区域,将这一部分区域标记一下,设为‘T’,跟真正的 ‘O’区分开。

    再遍历矩阵,把所有的‘T’还原为‘O’,把所有的‘O’填充为‘X’

    private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    private int m, n;
    
    public void solve(char[][] board) {
        if (board == null || board.length == 0) {
            return;
        }
    
        m = board.length;
        n = board[0].length;
    
        for (int i = 0; i < m; i++) {
            dfs(board, i, 0);
            dfs(board, i, n - 1);
        }
        for (int i = 0; i < n; i++) {
            dfs(board, 0, i);
            dfs(board, m - 1, i);
        }
    
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'T') {
                    board[i][j] = 'O';
                } else if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
            }
        }
    }
    
    private void dfs(char[][] board, int r, int c) {
        if (r < 0 || r >= m || c < 0 || c >= n || board[r][c] != 'O') {
            return;
        }
        board[r][c] = 'T';
        for (int[] d : direction) {
            dfs(board, r + d[0], c + d[1]);
        }
    }
    

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

    题目描述

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

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

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

    提示:

    1. 输出坐标的顺序不重要
    2. mn 都小于150

    示例:

    给定下面的 5x5 矩阵:
      太平洋 ~   ~   ~   ~   ~ 
           ~  1   2   2   3  (5) *
           ~  3   2   3  (4) (4) *
           ~  2   4  (5)  3   1  *
           ~ (6) (7)  1   4   5  *
           ~ (5)  1   1   2   4  *
              *   *   *   *   * 大西洋
    返回:
    [[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (上图中带括号的单元).
    

    解题思路

    因为要找到的点需要既能到太平洋(左边界和上边界)又能到大西洋(右边界和下边界),所以我们设置两个boolean[][]矩阵,分别记录一个点是否能到这两个大洋,最后遍历一遍数组,找到既能到太平洋又能到大西洋的坐标点即可。

    记录boolean[][] 矩阵方法如下:

    从高往低找可能不太方便,所以转换思路,以边界为起点,通过 DFS 往高处走,能走到的点说明就是能到大洋的点。

    注意:因为在 DFS 中需要用到matrix[][]的内容,所以我们需要定义一个成员变量作为全局变量使用。

    private int[][] matrix;
    private int m, n;
    private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    
    public List<int[]> pacificAtlantic (int[][] matrix) {
        List<int[]> ans = new ArrayList<>();
        if (matrix == null || matrix.length == 0) {
            return ans;
        }
        m = matrix.length;
        n = matrix[0].length;
        this.matrix = matrix;
        boolean[][] canReachP = new boolean[m][n];
        boolean[][] canReachA = new boolean[m][n];
    
        for (int i = 0; i < m; i++) {
            dfs(i, 0, canReachP);
            dfs(i, n - 1, canReachA);
        }
        for (int j = 0; j < n; j++) {
            dfs(0, j, canReachP);
            dfs(m - 1, j, canReachA);
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (canReachA[i][j] && canReachP[i][j]) {
                    ans.add(new int[]{i, j});
                }
            }
        }
        return ans;
    }
    
    private void dfs (int r, int c, boolean[][] canReach) {
        if (canReach[r][c]) {
            return;
        }
        canReach[r][c] = true;
        for (int[] d : direction) {
            int nextRow = r + d[0];
            int nextCol = c + d[1];
            if (nextRow < 0 || nextRow >= m || nextCol < 0 || nextCol >= n
                    || matrix[r][c] > matrix[nextRow][nextCol]) {
                continue;
            }
            dfs(nextRow, nextCol, canReach);
        }
    }
    
  • 相关阅读:
    [JSOI2007][BZOJ1031] 字符加密Cipher|后缀数组
    leetcode Flatten Binary Tree to Linked List
    leetcode Pascal's Triangle
    leetcode Triangle
    leetcode Valid Palindrome
    leetcode Word Ladder
    leetcode Longest Consecutive Sequence
    leetcode Sum Root to Leaf Numbers
    leetcode Clone Graph
    leetcode Evaluate Reverse Polish Notation
  • 原文地址:https://www.cnblogs.com/yuzhenzero/p/10730847.html
Copyright © 2011-2022 走看看