DFS的不足和BFS算法
虽然我们知道根据DFS算法我们可以找到所有的,由起始节点到目标节点的所有路径,但并不代表那条路是最短的或者是最佳的。就像我们上篇文章所说的一样,对于同一幅图,非递归算法找到的路径就明显比递归算法找的要短。
回顾我们之前提到的BFS的基本思想:从起始顶点开始,首先探索邻居节点,然后再移动到下一级邻居。
假设我们从a走到i,按照BFS算法,应该是
a -> d-> h ->i (并且这是一条最短的路径)
其BFS伪代码如下:
bfs from v1 to v2:
//建立一个队列q来存储走过的路径path(可以用vector来存储路径)
create a queue of paths (a vector), q
//将节点v1的路径入队
q.enqueue(v1 path)
//当q不为空,并且节点V2未被访问时
while q is not empty and v2 is not yet visited:
//将q中的元素移出队列,并且存入path中
path = q.dequeue()
//将路径中的最后元素赋值给节点型变量v
v = last element in path
//如果v未被访问
if v is not visited:
//标记节点V
mark v as visited
//如果节点是目标节点,停止执行
if v is the end vertex, we can stop.
//遍历V中所有未被标记的邻居
for each unvisited neighbor of v:
//将v节点的邻居作为最后的元素构成新的路径
make new path with v's neighbor as last element
//将新的路径排入队列中
enqueue new path onto q
没错 这是一个很复杂的步骤。理解都有点困难更别提记住了,但是我们可以注意到,这个实现过程跟DFS的非递归实现是非常类似的。下面我们也是同样的分析一下实现过程:
还是从a到i,front指针指向队头。
- 先建立一个vector,用来存储路径,建立一个队列将路径装入:
Vector<Vertex *> startPath
startPath.add(a)
q.enqueue(startPath)
- 此时队列中有元素a,已经访问的集合为空:
- 接下来到达循环语句:
in while loop:
//将队列中的元素取出,放到当前路径的变量中
curPath = q.dequeue() (path is a)
//由于队列中只有一个元素a,所以它的最后元素也是它本身
v = last element in curPath (v is a)
//标记V
mark v as visited
//将所有的未标记的路径入队q中
enqueue all unvisited neighbor paths onto q
这里注意,这里最后一句入队的是路径而不是节点,标记的才是及诶单。执行完毕后,队列跟访问集的情况如下:
- 此时队列不为空,直接从while循环开始。
in while loop:
curPath = q.dequeue() (path is ab)
v = last element in curPath (v is b)
mark v as visited
enqueue all unvisited neighbor paths onto q
根据队列的性质,FILO,所以最先出队列的是ab,ab路径中最后的元素是b,所以,标记b,并将b的所有邻居放入队列中去:
5.重复上述过程,直到路径中的最后的元素为目标元素 i 时,停止搜索。此时情况如下:
in while loop:
curPath = q.dequeue() (path is adhi)
v = last element in curPath (v is i)
found!
此时的路径 a -> d-> h ->i 也是最短的路径
总结
在对于需要找到最短路径的图来说,BFS是绝佳的选择。广度优先搜索将查找从起始节点可到达的所有节点。
●它将按距离递增的顺序访问它们。
●在n节点m边图中,需要时间O(m + n)并使用空间O(n)。
●但在实践中,空间使用率远高于DFS。
这里有个有趣的问题,如果我们将BFS思想运用在树的遍历中会得到什么结论?得到一种我们熟悉的遍历方式。树中可是没有环的。