[路飞]_BFS(广搜)&&DFS(深搜)

179 阅读4分钟

哈喽,又到了一周一打卡的时间了,肝了两天,算是弄懂了一丢丢的BFS&&DFS

先介绍一下 广度优先搜索算法 Breadth First Search (BFS),初步了解,其实有点像二叉树问题中用到的层序遍历时 使用队列结构来辅助实现解决问题, 广度优先,顾名思义是广,其重点就是在同一层中进行查找是否有符合解决问题的答案,一层处理完再到下一层中去

BFS相关题目

542. 01 矩阵

该题的意思就是将所有为0的格子到非0的格子所需要的走到格子数记录到该非0格子中

var updateMatrix = function(mat) {
    let cols = mat[0].length; // 列
    let rows = mat.length; // 行
    // 结果集
    let dist = new Array(rows).fill(0).map(() => new Array(cols).fill(0));
    // 记录每个格子的状态
    let vis =  new Array(rows).fill(false).map(() => new Array(cols).fill(false));
    // 当前格子可移动方向的坐标
    let pos = [
        [0,1],
        [0,-1],
        [1,0],
        [-1,0]
    ]
    // BFS实现所需的队列
    let quene = []
    // 对每个格子进行vis初始化
    for(let i = 0; i < rows; i++){
        for(let j = 0; j < cols; j++){
            if(mat[i][j] === 0){
                quene.push([i,j])
                vis[i][j] = true
            }
        }
    }
    while(quene.length){
        let [x,y] = quene.shift()
        // 通过格子可移动方向计算结果集中对应格子的步数
        for(let [i,j] of pos){
            let newX = x + i
            let newY = y + j
            if( newX < 0 || 
                newY < 0 || 
                newX >= rows || 
                newY >= cols || 
                vis[newX][newY]){
                continue;
            }
            dist[newX][newY] = dist[x][y] + 1
            vis[newX][newY] = true
            quene.push([newX,newY])
            
        }
    }
    return dist
};

752. 打开转盘锁

这题的意思就是在deadends中的字符串数字中是被锁住的,无法进行旋转 那这里的旋转的意思就是 如果当前数字是0 那就向上旋转为9 即 0 + 9 如果当前数字是 9 即向下旋转为 0 即 9 + 1 取模 为 0, 然后每旋转一次就记录一次,最后求出最小的旋转数, 那这里需要用到Set 的不会重复存储的特性,方便解题

var openLock = function(deadends, target) {
    let step = 0 // 步数
    let deadSet = new Set() // 存储无法旋转的字符串数字
    let vistSet = new Set() // 存储已经旋转存在过的数字
    for(let num of deadends){
        deadSet.add(num)
    }
    let quene = ['0000'] // 题目要求从0000开始旋转
    while(quene.length){
        let size = quene.length;
        while(size--){
            let val = quene.shift()
            if(val === target ){
                return step
            }
            if(deadSet.has(val) || vistSet.has(val)){
                continue;
            }
            vistSet.add(val)
            for(let j = 0; j < val.length; j++){
                let num = val[j] - '0' 
                // 求出当前旋转后的数字
                let up = (num + 1) % 10, 
                    dowm = (num + 9 )% 10
                // 拼接后push到队列中
                quene.push(val.substring(0,j) + up + val.substring(j+1))
                quene.push(val.substring(0,j) + dowm + val.substring(j+1))
            }
        }
        step++
    }
    return -1
};

1091. 二进制矩阵中的最短路径

该题就是说 在正方形的宫格中从0,0出发 分别可以在横纵对角线的方向进行前进,但途经为0的格子,求改路径途径的格子总数

var shortestPathBinaryMatrix = function(grid) {
    let depth = 0;
    // 是一个正方形
    let m = grid.length - 1;
    // 边界情况,如果开头或结尾是1的情况下,不论怎么走都不可能达到题目要求
    if (grid[0][0] == 1 || grid[m][m] == 1) return -1;
    // 如果只有一个元素的情况
    if (m == 0) return 1;
    let queue = [[0, 0]];
    grid[0][0] = 1
    // 由于增加了对角线的方向,那么固定走向坐标为8个
    let pos = [
        [0,-1],
        [0,1],
        [-1,0],
        [1,0],
        [-1,-1],
        [-1,1],
        [1,-1],
        [1,1],
    ]

    while(queue.length){
        depth++
        let size = queue.length
        console.log(queue)
        while(size--){
            let [curX,curY] = queue.shift()
            // 如果当前坐标等于m了 那说明到最后了,返回结果即可
            if (curX == m && curY == m) return depth;
            for(let [x,y] of pos){
                let newX = curX + x, newY = curY + y
                if( 
                    newX > m || 
                    newY > m || 
                    newX < 0 || 
                    newY < 0 || 
                    grid[newX][newY] == 1
                   ){
                    continue
                }
                queue.push([newX,newY])
                grid[newX][newY] = 1
            }
        }

    }
    return -1
};

到了深度优先搜索算法 Depth First Search(DFS), 我草率的理解就是不断的递归,分各种条件去递归

DFS相关题目

130. 被围绕的区域

该题就是将宫格中被X围绕的O 改为X,这里我采取的就是先将为大O的记录为小o,最后将小o记录为大O,大O全部改为X, 最先要处理的是四边的O因为四边的O肯定没有被X包围的,所以可以直接处理

 var solve = function(board) {
    // 创建二维数组,默认全部置0
    let m=board.length, n=board[0].length
    let dir = [
        [0,1],
        [0,-1],
        [1,0],
        [-1,0]
    ]
    let dfs = (i, j, arr) => {
        arr[i][j] = 'o'
        for(let [x,y] of dir) {
            let newX = x + i, newY = y + j;
            if( newX < 0 || 
                newY < 0 || 
                newX >= m || 
                newY >= n ||
                arr[newX][newY] != 'O'
             ){
                 continue;
             }
            dfs(newX, newY, arr)
        }
    }
    // 处理左右两边的O
    for(let i = 0; i < m; i++){
        if(board[i][0] === 'O' ){
            dfs(i, 0, board);
        }
        if(board[i][n-1] === 'O'){
            dfs(i, n-1, board)
        }
    }
    // 处理上下两边的O
    for(let i = 0; i < n; i++){
        if(board[0][i] === 'O' ){
            dfs(0, i, board);
        }
        if(board[m-1][i] === 'O'){
            dfs(m-1, i, board)
        }
    }
    // 将大O改为X 小o改为O
    for(let i = 0; i < m; i++){
        for(let j = 0; j < n; j++){
            if(board[i][j] == 'O'){
                board[i][j] = 'X'
            }
            if(board[i][j] == 'o'){
                board[i][j] = 'O'
            } 
        }
    }
};

473. 火柴拼正方形

这题,就是说用题目给的火柴棍来拼成正方形,但是给的火柴长度不等,数量也不等,这里桶这么一个解题思路,由于是正方形,那么四边就是相等的,那就是说我可以用4个大小相同的桶来表示火柴衔接起来的长度,如果长度都刚好能表示桶的大小,那直接返回true即可,但是会出现桶只有3的大小,那火柴是长度2的大小,就会出现填不满问题,就是可能会有余量大于最小火柴的长度,这里采取剪枝的方式

let dfs = function(index, arr, ms) {
    if (index == -1 ) return true
    for(let i = 0; i < 4; i++) {
        if (arr[i] < ms[index]) continue
        if (arr[i] == ms[index] || arr[i] - ms[index] >= ms[0]) {
            // 剪枝
            arr[i] -= ms[index]
            if (dfs(index - 1, arr, ms)) return true
            arr[i] += ms[index]
        }
    }
    return false
}
var makesquare = function(matchsticks) {
    let sum = 0 // 总和
    for(let item of matchsticks) sum += item
    if (sum % 4) return false;
    // 从小到大排序
    matchsticks.sort((a, b) =>  a -b )
    // 桶
    let arr = new Array(4).fill(sum / 4)
    // 从后往前搜索,先将大的往里塞
    return dfs(matchsticks.length - 1, arr, matchsticks)
};