这是我参与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)
}
}
}
到此,我们已经一步步的实现了一个最基本的广度优先搜索算法。
总结
从上面实现的过程中,我们遍历的图是邻接表
,
核心思想是:从 指定顶点
出发,挨个的探索顶点,访问到某个顶点时,一定要访问其所有的邻接顶点。具体步骤如下:
- 首先是从一个指定的顶点出发,进行访问;
- 同时创建一个队列用来存储已经访问过的顶点,利用了队列的先进先出特性;
- 也就说,在队列的第一项要出去的时候,它已经是被访问过的了;
- 然后拿到此顶点后,访问其邻接顶点列表,依次将邻接顶点进行标记;
- 最后将此顶点的邻接顶点列表遍历完之后,说明此顶点已经被完全探索过了,将其状态标记为黑色。