算法笔记——dfs,bfs

深度优先和广度优先‘(dfs、bfs)是最常用的两种搜索方法,在各类算法竞赛中也是频频出现,(可参见2017蓝桥杯的前4题均是搜索题),所以掌握两种搜索的适用场合和使用方法是很有必要的。
适用场合:
bfs:常用在搜索最短路径的情景,一旦搜索到可以结束,因为其深度一定是最短的。dfs也可以但是就必须全部搜索完才能判定最短的路径。
dfs:所需要维护的空间少,这个跟其每次只需要维护一个方向的记录有关。

使用方法:
(即使用的数据结构和存储方式)
bfs:队列
dfs:栈
具体见题目:
围棋游戏:对于给定的二维数组,其中只有’x’和’o’,现在需要将其中被包围的’o’替换成’x’。(在边缘的不算被包围)
所以我们要找到矩阵边缘的’o’,并以此为根找到与之相通的’o’,这些是不被包围的,那么剩下的就需要进行替换。

思路一:bfs

public class Pos{
	        int i;
	        int j;
	        Pos(int i, int j) {
	            this.i = i;
	            this.j = j;
	        }
	    }
	    public void solve(char[][] board) {
	        if (board == null || board.length == 0) return;
	        int m = board.length;
	        int n = board[0].length;
	        for (int i = 0; i < m; i++) {
	            for (int j = 0; j < n; j++) {
	                // 从边缘第一个是o的开始搜索
	                boolean isEdge = i == 0 || j == 0 || i == m - 1 || j == n - 1;
	                if (isEdge && board[i][j] == 'O') {
	                    bfs(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';
	                }
	                if (board[i][j] == '#') {
	                    board[i][j] = 'O';
	                }
	            }
	        }
	    }

	    public void bfs(char[][] board, int i, int j) {
	        Queue<Pos> queue = new LinkedList<>();
	        queue.add(new Pos(i, j));
	        board[i][j] = '#';
	        while (!queue.isEmpty()) {
	            Pos current = queue.poll();
	            // 上
	            if (current.i - 1 >= 0 
	                && board[current.i - 1][current.j] == 'O') {
	                queue.add(new Pos(current.i - 1, current.j));
	                board[current.i - 1][current.j] = '#';
	              	// 没有continue.
	            }
	            // 下
	            if (current.i + 1 <= board.length - 1 
	                && board[current.i + 1][current.j] == 'O') {
	                queue.add(new Pos(current.i + 1, current.j));
	                board[current.i + 1][current.j] = '#';      
	            }
	            // 左
	            if (current.j - 1 >= 0 
	                && board[current.i][current.j - 1] == 'O') {
	                queue.add(new Pos(current.i, current.j - 1));
	                board[current.i][current.j - 1] = '#';
	            }
	            // 右
	            if (current.j + 1 <= board[0].length - 1 
	                && board[current.i][current.j + 1] == 'O') {
	                queue.add(new Pos(current.i, current.j + 1));
	                board[current.i][current.j + 1] = '#';
	            }
	        }
	    
	}

因为是广度优先遍历,每次都将能延伸的点探索完,所以要弹出队首(queue.poll()),并将这个点符合要求的一层搜索都加入队尾。这样可以保证搜索是一层一层的。

思路二:dfs

class Solution {
    public class Pos{
        int i;
        int j;
        Pos(int i, int j) {
            this.i = i;
            this.j = j;
        }
    }
    public void solve(char[][] board) {
        if (board == null || board.length == 0) return;
        int m = board.length;
        int n = board[0].length;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 从边缘第一个是o的开始搜索
                boolean isEdge = i == 0 || j == 0 || i == m - 1 || 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';
                }
                if (board[i][j] == '#') {
                    board[i][j] = 'O';
                }
            }
        }
    }

    public void dfs(char[][] board, int i, int j) {
        Stack<Pos> stack = new Stack<>();
        stack.push(new Pos(i, j));
        board[i][j] = '#';
        while (!stack.isEmpty()) {
            // 取出当前stack 顶, 不弹出.
            Pos current = stack.peek();
            // 上
            if (current.i - 1 >= 0 
                && board[current.i - 1][current.j] == 'O') {
                stack.push(new Pos(current.i - 1, current.j));
                board[current.i - 1][current.j] = '#';
              continue;
            }
            // 下
            if (current.i + 1 <= board.length - 1 
                && board[current.i + 1][current.j] == 'O') {
                stack.push(new Pos(current.i + 1, current.j));
                board[current.i + 1][current.j] = '#';      
                continue;
            }
            // 左
            if (current.j - 1 >= 0 
                && board[current.i][current.j - 1] == 'O') {
                stack.push(new Pos(current.i, current.j - 1));
                board[current.i][current.j - 1] = '#';
                continue;
            }
            // 右
            if (current.j + 1 <= board[0].length - 1 
                && board[current.i][current.j + 1] == 'O') {
                stack.push(new Pos(current.i, current.j + 1));
                board[current.i][current.j + 1] = '#';
                continue;
            }
            // 如果上下左右都搜索不到,本次搜索结束,弹出stack
            stack.pop();
        }
    }
}

使用栈思路会很清晰:只要有一个符合要求的探索就continue,符合深度优先的定义。弹出的时机:如果上下左右都搜索不到,本次搜索结束,弹出stack。与bfs不同,bfs是每次都会弹出队首,因为以后不会用到这个点了。

最后,还有一种递归的方法,其实也是dfs的一种,但是用的是递归函数。

public void solve(char[][] board) {
	        if (board == null || board.length == 0) return;
	        int m = board.length;
	        int n = board[0].length;
	        for (int i = 0; i < m; i++) {
	            for (int j = 0; j < n; j++) {
	                // 从边缘o开始搜索
	                boolean isEdge = i == 0 || j == 0 || i == m - 1 || 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';
	                }
	                if (board[i][j] == '#') {
	                    board[i][j] = 'O';
	                }
	            }
	        }
	    }

	    public void dfs(char[][] board, int i, int j) {
	        if (i < 0 || j < 0 || i >= board.length  || j >= board[0].length || board[i][j] !='O') {
	            //  board[i][j] !='O' 说明已经搜索过了. 
	            return;
	        }
	        board[i][j] = '#';
	        dfs(board, i - 1, j); // 上
	        dfs(board, i + 1, j); // 下
	        dfs(board, i, j - 1); // 左
	        dfs(board, i, j + 1); // 右
	    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值