[leetcode][dfs]深度优先搜索算法总结

dfs算法通常使用递归实现,逻辑清晰。在递归开头写一个跳出条件,然后继续向下递归遍历即可。要用一个额外的空间记录节点访问情况,遍历过的地方不再遍历,防止陷入循环,或者在原空间上进行修改。下面给出图上的dfs算法实现,与树有关的算法在树的博客中单独总结。

200.岛屿数量

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

输入:
11110
11010
11000
00000

输出: 1

输入:
11000
11000
00100
00011

输出: 3

线性扫描整个二维网格,如果一个结点包含'1',则以其为根结点启动深度优先搜索。在深度优先搜索过程中,每个访问过的结点被标记为'0'。计数启动深度优先搜索的根结点的数量,即为岛屿的数量。在搜索时遇到超过边界或节点值为‘1’时终止搜索,否则向四个方向继续进行dfs。

class Solution {
    public int numIslands(char[][] grid) {
        int ret = 0;
        for(int i=0;i=rowMax || col>=colMax || grid[row][col] == '0') {
            return;
        }
        grid[row][col] = '0';
        dfs(grid,row-1,col);
        dfs(grid,row+1,col);
        dfs(grid,row,col-1);
        dfs(grid,row,col+1);

    }
}

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'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

在边界进行dfs,将‘O’替换为‘#’,可将边界相邻的全部替换为‘#’,最后遍历一遍矩阵,将‘O’替换为‘X’,将‘#’替换为‘O’。

class Solution {
    public void solve(char[][] board) {
        if (board.length == 0) {
            return;
        }
        int rowMax = board.length;
        int colMax = board[0].length; 
        for(int i=0;i=rowMax || col>=colMax || board[row][col]=='X' 
        || board[row][col]=='#') {
            return;
        }
        board[row][col] = '#';
        dfs(board,row-1,col);
        dfs(board,row+1,col);
        dfs(board,row,col-1);
        dfs(board,row,col+1);
    }
}

133.克隆图

给你无向连通图中一个节点的引用,请你返回该图的 深拷贝(克隆)。

class Node {
    public int val;
    public List neighbors;
}

使用哈希表存储克隆图中的节点,key为val,值为克隆图的Node,克隆当前节点后,对其邻居节点进行dfs克隆。

class Solution {
    public Node cloneGraph(Node node) {
        if(node == null) {
            return null;
        }
        Map map = new HashMap<>();
        Node root = new Node(node.val);
        map.put(root.val,root);
        dfs(map,node,root);
        return root;
    }

    public void dfs(Map map,Node oldNode,Node newNode) {
        for(Node node:oldNode.neighbors) {
            if (!map.containsKey(node.val)) {
                map.put(node.val,new Node(node.val));
                dfs(map,node,map.get(node.val));
            }
            newNode.neighbors.add(map.get(node.val));
        }
    }

}

329.矩阵中的最长递增路径

给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

输入: nums = 
[
  [9,9,4],
  [6,6,8],
  [2,1,1]

输出: 4 
解释: 最长递增路径为 [1, 2, 6, 9]。

输入: nums = 
[
  [3,4,5],
  [3,2,6],
  [2,2,1]

输出: 4 
解释: 最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。

朴素的想法,从每一个点开始进行dfs,返回其中的最大值,但这种方法有很多重复计算,可能某次的路径为之前的子路径,使得算法运行超时。使用记忆化dfs的方法,定义cache矩阵,cache[i][j]记录位置(i,j)点开始的最长递增路径长度。这样也不用维护访问数组。当cache[i][j]已经被计算过,再次遍历到这直接返回即可。

class Solution {
    public int longestIncreasingPath(int[][] matrix) {
        if(matrix.length == 0) {
            return 0;
        }
        int m = matrix.length;
        int n = matrix[0].length;
        int[][] cache = new int[m][n];
        int ret = 0;
        for(int i=0;i=matrix.length || j>=matrix[0].length) {
            return 0;
        }
        if (cache[i][j] != 0) {  //已经计算过的直接返回
            return cache[i][j];
        }
        int ret = 0;
        // 向更大的方向遍历
        if(i-1>=0 && matrix[i-1][j] > matrix[i][j]) {
            int cur = dfs(i-1,j,matrix,cache);
            ret = Math.max(ret,cur);
        }
        if(i+1 matrix[i][j]) {
            int cur = dfs(i+1,j,matrix,cache);
            ret = Math.max(ret,cur);
        }
        if(j-1>=0 && matrix[i][j-1] > matrix[i][j]) {
            int cur = dfs(i,j-1,matrix,cache);
            ret = Math.max(ret,cur);
        }
        if(j+1 matrix[i][j]) {
            int cur = dfs(i,j+1,matrix,cache);
            ret = Math.max(ret,cur);
        }
        cache[i][j] = ret+1;  //加上当前这个格子
        return ret+1;
    }
}

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

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

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

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

给定下面的 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]] (上图中带括号的单元).

从海洋反向流回陆地,即向更高的地方流动。维护两个矩阵,分别表示可以被太平洋和大西洋流到的地方,最后返回两个海洋都能流动到的地点的坐标。

class Solution {
    public List> pacificAtlantic(int[][] matrix) {
        List> ret = new ArrayList<>();
        if(matrix.length == 0){
            return ret;
        }
        boolean[][] ocean1 = new boolean[matrix.length][matrix[0].length];
        boolean[][] ocean2 = new boolean[matrix.length][matrix[0].length];
        for(int i=0;i p = new ArrayList<>();
                    p.add(i);
                    p.add(j);
                    ret.add(p);
                }
            }
        }
        return ret;
    }

    public void dfs(int[][] matrix,int i,int j,boolean[][] ocean) {
        // 流过的地方不再流
        if(ocean[i][j]) {
            return;
        }
        ocean[i][j] = true;
        if(i-1>=0 && matrix[i-1][j] >= matrix[i][j]) {
            dfs(matrix,i-1,j,ocean);
        }
        if(i+1= matrix[i][j]) {
            dfs(matrix,i+1,j,ocean);
        }
        if(j-1>=0 && matrix[i][j-1] >= matrix[i][j]) {
            dfs(matrix,i,j-1,ocean);
        }
        if(j+1= matrix[i][j]) {
            dfs(matrix,i,j+1,ocean);
        }
    }

    
}

 

 

 

 

 

你可能感兴趣的:(leetcode,leetcode,深度优先搜索,dfs)