数据结构之图的广度优先搜索

252 阅读3分钟

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

广度优先搜索

// 将图的所有顶点全部初始化为白色
const colors = {
    white: 0,
    gray: 1,
    black: 2
}
const initializeColor = vertices => { 
    const color = {}; 
    for (let i = 0; i < vertices.length; i++) { 
        color[vertices[i]] = colors.white; 
    } 
    return color; 
};
  • 首先,我们要知道广度优先搜索需要标注被访问过的顶点。用来记录哪些顶点被访问过,哪些未被访问。
  • 那么,就容易想到我们之前的准备工作中的colors,并将所有的顶点先初始化为都未被访问,即标注为白色。
  • 接下来在我们之前创建的图类中,创建bfs方法,它接受两个参数:即顶点v和回调函数callback。
  • bfs方法的接收的顶点v是必须的,因为我们必须确定搜索的起始点
this.bfs = function (v, callback) {
    // 拿到初始化标记过状态之后的顶点的集合,是一个对象数组,将顶点作为key,颜色作为value
    let color = initializeColor();
    // 初始化一个队列
    let queue = new Queue();
    // 将未访问过的顶点v,此时v已经被访问到了,推入队列中
    queue.enqueue(v);
    // 循环队列
    while(!.queue.isEmpty()) {
        // 如果队列非空的话,继续执行以下的操作
        // 移除队列的第一项 u
        let u = queue.dequeue();
        // adjList字典中存储的是顶点和它的邻接顶点列表,所以我们拿到顶点u的邻接列表
        let neighbors = adjList.get(u);
        // 将访问过的顶点u的状态变更为灰色
        color[u] = 'gray'
        // 接着探索 顶点u的邻接列表
        for (let i = 0; i < neighbors.length; i++) {
            // 取出每个邻接顶点
            let w = neighbors[i]
            // 如果该顶点是白色的,则说明未被访问过,那么此时将其状态颜色变为gray,并将其推入队列中
            if (color[w] === 'white') {
                color[w] = 'gray'
                queue.enqueue(w)
            }
        }
        // 当我们将顶点u的所以邻接顶点完全访问完之后,将顶点u的状态变为黑色
        color[u] = 'black';
        // 如果有回调的话,执行回调函数
        if (callback) {
            callback(u)
        }
        
    }
}

到此,我们已经一步步的实现了一个最基本的广度优先搜索算法。

总结

从上面实现的过程中,我们遍历的图是邻接表, 核心思想是:从 指定顶点 出发,挨个的探索顶点,访问到某个顶点时,一定要访问其所有的邻接顶点。具体步骤如下:

  • 首先是从一个指定的顶点出发,进行访问;
  • 同时创建一个队列用来存储已经访问过的顶点,利用了队列的先进先出特性;
  • 也就说,在队列的第一项要出去的时候,它已经是被访问过的了;
  • 然后拿到此顶点后,访问其邻接顶点列表,依次将邻接顶点进行标记;
  • 最后将此顶点的邻接顶点列表遍历完之后,说明此顶点已经被完全探索过了,将其状态标记为黑色。