前端面试算法篇——BFS 宽度优先遍历

468 阅读1分钟

一、几个简单的BFS问题

1、 层级遍历二叉树(leetcode#102)

var levelOrder = function(root) {
    if(!root) return[]
    const q = [root]
    const res = []
    while(q.length) {
        const sz = q.length
        const lev = []
        for(let i = 0; i < sz; i++) {
            const node = q.shift()
            lev.push(node.val)
            if(node.left) q.push(node.left)
            if(node.right) q.push(node.right)
        }
        res.push(lev)
    }
    return res
};

2、二叉树的最小深度

var minDepth = function(root) {
    if(!root) return 0
    const q = [root]
    let step = 1
    while(q.length) {
        const sz = q.length
        for(let i = 0; i < sz; i++) {
            const node = q.shift()
            if(!node.left && !node.right) {
                return step
            }
            if(node.left) q.push(node.left)
            if(node.right) q.push(node.right)
        }
        step++
    }
    return step
};

实际上使用dfs也可以以相同的时间复杂度数量级O(N)解决这个问题

var minDepth = function(root) {
    if(!root) return 0
    if(root.left && !root.right) return minDepth(root.left)+1
    if(!root.left && root.right) return minDepth(root.right)+1
    return Math.min(minDepth(root.left)+1, minDepth(root.right)+1)
};

虽然都是O(N),但是BFS的平均时间肯定是比DFS小的,因为BFS在找到最短路径时就会停止。但是DFS的空间复杂度更低,使用递归栈来记录路径的方法最坏的时间复杂度也就是树的深度O(logN)。

二、开锁问题(leetcode#752)

简单介绍一下,一把锁四个槽位,每个位置可以滑动到从0-9,但是有一个deadends数组,碰到这里面的数字就会死亡,求不碰到deadends且拨到target的最小步数。

var plusOne = function(cur,idx) {
    const listCode = cur.split('').map(num=>parseInt(num))
    if(listCode[idx] === 9) {
        listCode[idx] = 0
    } else {
        listCode[idx] += 1
    }
    return listCode.join('')
}

var minusOne = function(cur,idx) {
    const listCode = cur.split('').map(num=>parseInt(num))
    if(listCode[idx] === 0) {
        listCode[idx] = 9
    } else {
        listCode[idx] -= 1
    }
    return listCode.join('')
}

var openLock = function(deadends, target) {
    const q = ['0000']
    let step = 0
    const visited = new Set(deadends)
    if(visited.has('0000')) return -1
    while(q.length) {
        const sz = q.length
        for(let i = 0; i < sz; i++) {
            const curCode = q.shift()
            if(curCode === target) return step
            // 对于每一个状态code,都有八个变化的方向, 将八个方向获得并推进队列
            for(let j = 0; j < 4; j++){
                const up = plusOne(curCode, j)
                const down = minusOne(curCode, j)
                if(!visited.has(up)){
                    q.push(up)
                    visited.add(up)
                }
                if(!visited.has(down)){
                    q.push(down)
                    visited.add(down)
                }
            }
        }
        step++
    }
    return -1
};

在这道题,我们使用一个hashmap来避免走回头路,同样deadends也是我们碰到就要抛弃的,所以deadends就可以作为hashmap的初始值。我们把开锁过程抽象成了一个图,在图的每个节点都有8种可能的方向(虽然有的方向可能是deadend),我们要找到从"0000"到target的最短距离。