DFS和BFS专题

130. 被围绕的区域

总的思想是寻找边界条件,使用标记数组。

class Solution {
    // 将需要的变量设置为全局变量
    char[][] board;
    int rows;
    int cols;
    boolean[][] visited;
    public void solve(char[][] board) {
        /**
        分析:
        题目中提示的很明显要处理边界条件。
        边界条件具体来说包括四个:第一行,第一列,最后一行,最后一列。和这四个边界相邻的O也要处理。
        那么可以使用一个visisted二维数组来标记是否是边界满足值
         */
         this.board = board;
         rows = board.length;
         cols = board[0].length;
         this.visited = new boolean[rows][cols];
         // 遍历第一行和最后一行
         for(int i = 0; i < cols; i++){
             dfs(0,i);
             dfs(rows - 1,i);
         }
         // 遍历第一列和最后一列
         for(int j = 0; j < rows; j++){
             dfs(j,0);
             dfs( j,cols - 1);
         }
         // 遍历整个二维数组
         for(int i = 1; i < rows; i++){
             for(int j = 1; j < cols; j++){
                 // 发现非边界值O
                 if(board[i][j] == 'O' && !visited[i][j]){
                     board[i][j] = 'X';
                 }
             }
         }
    }
    public void dfs(int row,int col){
        // 不在区域内,不等于O,已经访问过的,直接return掉
        if(!inArea(row,col) || board[row][col] != 'O' || visited[row][col]){
            return;
        }
        visited[row][col] = true;
        dfs(row+1,col);
        dfs(row-1,col);
        dfs(row,col-1);
        dfs(row,col+1);
    }
    public boolean inArea(int row,int col){
        return row>=0 && row < rows && col >= 0 && col<cols;
    }
}

200. 岛屿数量(dfs)

class Solution {
    boolean[][] visited;
    public int numIslands(char[][] grid) {
        /**
        分析:
        和130题的思路一致,使用标记数组标记是否已经遍历过
         */
         int rows = grid.length;
         int cols = grid[0].length;
         this.visited = new boolean[rows][cols];
         int num = 0;
         for(int i = 0; i < rows; i++){
             for(int j = 0; j < cols; j++){
                 if(grid[i][j] == '1' && !visited[i][j]){
                     // 是陆地并且没有被标记过,则dfs一下,并且+1
                     dfs(grid,i,j);
                     num++;
                 }
             }
         }
         return num;
    }
    public void dfs(char[][] grid,int row ,int col){
        // 递归出口是跑出边界外,不为1,已经被访问过了
        if(row < 0 || row >= grid.length || col < 0 ||col >= grid[0].length){
            return ;
        }
        if(grid[row][col] != '1' || visited[row][col]) return;
        // 标记访问
        visited[row][col] = true;
        // 四个方向递归遍历
        dfs(grid,row+1,col);
        dfs(grid,row-1,col);
        dfs(grid,row,col+1);
        dfs(grid,row,col-1);
    }
}

429. N 叉树的层序遍历(bfs)

就是使用普通的bfs

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    public List<List<Integer>> levelOrder(Node root) {
        List<List<Integer>> res = new ArrayList<>();
        if(root == null) return res;
        Queue<Node> queue = new LinkedList<>();
        queue.offer(root);
        while( !queue.isEmpty()){
            int size = queue.size();
            List<Integer> temp = new ArrayList<>();
            for(int i = 0; i < size; i++){
                Node cur = queue.poll();
                temp.add(cur.val);
                if(cur.children != null){
                    // 孩子节点依次进队
                    for(int j = 0; j < cur.children.size(); j++){
                        queue.offer(cur.children.get(j));
                    }

                }
            }
            res.add(temp);
        }
        return res;
    }
}

463. 岛屿的周长

本题有两种解法,第一种解法不具备普适性,思路清奇。使用普通遍历,遇到小岛(‘1’)就累加,同时判断右侧和下侧是否有小岛,有的话累加边。最后的结果就是4*小岛个数 - 2 * 公共边。
第二种解法具备普适性,设置访问数组,在遍历过程中,遇到已访问陆地,则周长不变,return 0,遇到水域或者越界了,返回周长1 ,最后通过四个方向累加周长,返回周长。示意图如下:
在这里插入图片描述
解法一:普通遍历+数学公式

class Solution {
    public int islandPerimeter(int[][] grid) {
        /**
        分析:
        普通遍历,还得判定相应的数学公式:4*小岛个数 - 2 * 公共边
         */
         if(grid == null || grid.length == 0) return 0;
         int rows = grid.length;
         int cols = grid[0].length;
         int isLand = 0;
         int edge = 0;
         // 普通遍历
         for(int i = 0; i < rows; i++){
             for(int j = 0; j < cols; j++){
                 if(grid[i][j] == 1){
                     // 遇到了 陆地
                    isLand++;
                    // 判断下侧和右边有没有小岛
                    if(i < rows - 1 && grid[i+1][j] == 1) {
                        // 边+1
                        edge++;
                    }
                    if( j < cols - 1 && grid[i][j+1] == 1){
                        edge++;
                    }
                 }
             }
         }
         return 4 * isLand - 2 * edge;

    }
}

解法二:dfs+标记数组

class Solution {
    public int islandPerimeter(int[][] grid) {
        // 现在使用dfs遍历法,思路是遇到水域或者遇到边界,那么周长就+1
        int rows = grid.length;
        int cols = grid[0].length;
        boolean[][] visited = new boolean[rows][cols];
        for(int i = 0; i < rows; i++){
            for(int j = 0; j < cols; j++){
                if(grid[i][j] == 1 && !visited[i][j]){
                    return dfs(grid,i,j,visited);
                }
            }
        }
        return 0;
    }
    public int dfs(int[][] grid,int i,int j,boolean[][] visited){
        // 遇到边界了
        if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length) return 1;
        // 遇到水域了
        if(grid[i][j] == 0) return 1;
        // 遇到已经被访问的小岛
        if(visited[i][j]) return 0;
        // 标记访问
        visited[i][j] = true;
        // 定义周长
        int res = 0;
        // 四个方向递归遍历
        res += dfs(grid,i+1,j,visited);
        res += dfs(grid,i-1,j,visited);
        res += dfs(grid,i,j+1,visited);
        res += dfs(grid,i,j-1,visited);
        return res;
    }
}

589. N 叉树的前序遍历

没啥好说的,理解清除递归逻辑,很容易写出来

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> preorder(Node root) {
        if(root == null) return res;
        dfs(root);
        return res;
    }
    public void dfs(Node root){
        if(root == null) return;
        res.add(root.val);
        for(int i = 0; i < root.children.size(); i++){
            dfs(root.children.get(i));
        }
        
    }
}

590. N 叉树的后序遍历

/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    List<Integer> res = new ArrayList<>();
    public List<Integer> postorder(Node root) {
        if(root == null) return res;
        dfs(root);
        return res;

    }
    public void dfs(Node root){
        if(root == null) return;
        if(root.children != null){
            for(int i = 0; i < root.children.size(); i++){
                dfs(root.children.get(i));
            }
        }
        res.add(root.val);
    }
}

695. 岛屿的最大面积

总体来说是套小岛问题的模板

class Solution {
    boolean[][] visited;
    public int maxAreaOfIsland(int[][] grid) {
        /**
        分析:
        和之前求岛屿数量有一点区别,就是这里是不断更新最大岛屿数目的
         */
         if(grid == null || grid.length == 0) return 0;
         int rows = grid.length;
         int cols = grid[0].length;
         visited = new boolean[rows][cols];
         int res = 0;
         for(int i = 0; i < rows; i++){
             for(int j = 0; j < cols; j++){
                 if(grid[i][j] == 1 && !visited[i][j]){
                     // 每次更新搜索到的最大小岛数目
                    res = Math.max(res,dfs(grid,i,j)) ;
                 } 
             }
         }
         return res;
    }
    public int dfs(int[][] grid, int i,int j){
        // 越界了
        if( i < 0 || i >= grid.length || j <0 || j >= grid[0].length) return 0;
        // 遇到水或者已经访问过了
        if(grid[i][j] == 0 || visited[i][j]) return 0;
        int maxIsland = 0;
        // 设置标记
        visited[i][j] = true;
        // 四个方向遍历
        maxIsland += dfs(grid,i+1,j);
        maxIsland += dfs(grid,i-1,j);
        maxIsland += dfs(grid,i,j+1);
        maxIsland += dfs(grid,i,j-1);
        // 这里很关键,本层遍历成功,小岛个数要+1
        return maxIsland +1;

    }
}

994. 腐烂的橘子(多源BFS)

class Solution {
    public int orangesRotting(int[][] grid) {


    // 边界 长宽
    int M = grid.length;
    int N = grid[0].length;
    Queue<int[]> queue = new LinkedList<>();

    // count 表示新鲜橘子的数量
    int count = 0; 

    // 遍历二维数组 找出所有的新鲜橘子和腐烂的橘子
    for (int r = 0; r < M; r++) {
        for (int c = 0; c < N; c++) {
            // 新鲜橘子计数
            if (grid[r][c] == 1) {
                count++;
                // 腐烂的橘子就放进队列
            } else if (grid[r][c] == 2) {
                // 缓存腐烂橘子的坐标
                queue.add(new int[]{r, c});
            }
        }
    }

    // round 表示腐烂的轮数,或者分钟数
    int round = 0; 

    // 如果有新鲜橘子 并且 队列不为空
    // 直到上下左右都触及边界 或者 被感染的橘子已经遍历完
    while (count > 0 && !queue.isEmpty()) {

        // BFS 层级 + 1
        round++;

        // 拿到当前层级的腐烂橘子数量, 因为每个层级会更新队列
        int n = queue.size();

        // 遍历当前层级的队列
        for (int i = 0; i < n; i++) {

            // 踢出队列(拿出一个腐烂的橘子)
            int[] orange = queue.poll();

            // 恢复橘子坐标
            int r = orange[0];
            int c = orange[1];

            // ↑ 上邻点 判断是否边界 并且 上方是否是健康的橘子
            if (r-1 >= 0 && grid[r-1][c] == 1) {
                // 感染它 
                grid[r-1][c] = 2;
                // 好橘子 -1 
                count--;
                // 把被感染的橘子放进队列 并缓存
                queue.add(new int[]{r-1, c});
            }
            // ↓ 下邻点 同上
            if (r+1 < M && grid[r+1][c] == 1) {
                grid[r+1][c] = 2;
                count--;
                queue.add(new int[]{r+1, c});
            }
            // ← 左邻点 同上
            if (c-1 >= 0 && grid[r][c-1] == 1) {
                grid[r][c-1] = 2;
                count--;
                queue.add(new int[]{r, c-1});
            }
            // → 右邻点 同上
            if (c+1 < N && grid[r][c+1] == 1) {
                grid[r][c+1] = 2;
                count--;
                queue.add(new int[]{r, c+1});
            }
        }
    }

    // 如果此时还有健康的橘子
    // 返回 -1
    // 否则 返回层级
    if (count > 0) {
        return -1;
    } else {
        return round;
    }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值