对「深搜/广搜」的感悟🤑

935 阅读3分钟

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

BFS/DFS

广搜

  • 以v为起始点,由近至远依次访问和v路径想通且路径长度为1,2,...的顶点,是一种分层的查找过程
  • 为了实现逐层的访问,算法必须借助一个辅助队列(vis 数组和队列)
  • 算法过程可以看做是图上火苗传播的过程:最开始只有起点着火了,在每一时刻,有火的节点都向它相邻的所有节点传播火苗。 时间复杂度 O(n+m) 空间复杂度 O(n)

图片.png

图片.png

深搜

  • 所谓'深度优先',就是说每次都尝试向'更深的节点走'
  • DFS 最显著的特征在于其 递归调用自身 。同时与 BFS 类似,DFS 会对其访问过的点打上访问标记
  • 在遍历图时跳过已打过标记的点,以确保 每个点仅访问一次
  • 时间复杂度 O(n+m)
  • 空间复杂度 O(n)其中n表示点数,m表示边数,复杂度包含了栈空间,栈空间的空间复杂度是 O(n)

图片.png

图片.png

重头戏两道题

1

  • 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
输入: m = 2, n = 3, k = 1
输出: 3
  • 首先我们明确,是否可以去使用深搜从左上角开始出发,只有两个方向那就是下和右
  • 想出辅助函数是深搜,再去思考终止条件是什么,什么时候返回,来看下面的代码
class Solution {
    boolean[][] v;
    public int movingCount(int m, int n, int k) {
    // 用来标记是否走过
        v = new boolean[m][n];
        return dfs(m,n,0,0,k);
    }

    int dfs(int m,int n,int x,int y,int k) {
       // 特判不成立情况
        if (x < 0 || x >= m || y < 0 || y >= n || v[x][y] ||
        (x%10 + x/10 + y%10 + y/10) > k) return 0;
        v[x][y] = true;
        // 返回成功情况(右和下两个方向)
        return 1 + dfs(m,n,x+1,y,k) + dfs(m,n,x,y+1,k);
    }
}

2

  • 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
  • 例如,在下面的 3×4 的矩阵中包含单词 "ABCCED"(单词中的字母已标出)。
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true

图片.png

  • 首先思考是否可以使用深搜,每个字母是否都有四个方向
  • 回想第一题,去确定辅助函数的,终止条件是什么,什么时候返回,看代码!
class Solution {
    public boolean exist(char[][] board, String word) {
        char[] words = word.toCharArray();
        // 遍历整个二维数组
        for (int i = 0; i < board.length; i++) {
            for (int j = 0 ; j < board[0].length; j++) {
                if (dfs(board,words,i,j,0)) return true;
            }
        }
        return false;
    }

    boolean dfs (char[][] board,char[] word,int i,int j,int k) {
        if (i < 0 || i >= board.length || j < 0 || j>= board[0].length || 
        board[i][j] != word[k]) return false;

        if(word.length - 1 == k) return true;
        // 特殊占位符
        board[i][j] = '\0';
        // 上下左右四个方向
        boolean res = 
        dfs(board,word,i+1,j,k+1) || 
        dfs(board,word,i-1,j,k+1) || 
        dfs(board,word,i,j+1,k+1) || 
        dfs(board,word,i,j-1,k+1);
        // 还原回来数据
        board[i][j] = word[k];

        return res;
    }
}

总结

  • 诸如上面的两道题都是矩阵里面去搜索,其实我们都可以使用深搜
  • 按照老规矩去写辅助函数,确定好出口在哪里,特判条件,到底该设置几个参数
  • 最后就是完整地写出了DFS!🐯