在上一篇中我们学习了数据结构中的图,以及对图进行的动态操作,本章我们将会介绍图非常重要的算法BFS与DFS
对于图的遍历,结果相同,区别在于遍历的策略(节点的访问顺序)
广度优先搜索(BFS):策略:越早被访问的节点,其邻居越早被选用。
简单来说,就是一个节点被访问后,会优先遍历这个节点的邻居。
直观点,就是围绕初节点,一层一层的遍历,和二叉树中的层次遍历也是BFS。
时间复杂度:首先对所有节点和边复位需要O(n+e),枚举每个节点需要O(n),所以,最终时间复杂度需要O(n+e)。
应用场景:考虑全局的节点,比如最短路径。
由于迭代先进先出的顺序非常适合BFS,所以这里我们使用队列来暂时保存节点。
图片来自数据结构(C++语言版)邓俊辉编著
代码如下:
template<typename Tv, typename Te>
void bfs(int start){
reset(); // 重置图
int v = start; int clock = 0;
do{
if(UNDISCOVERED == status(v)){
BFS(v, clock);
}
}while(s != (v = (++v % n)));
}
template<typename Tv, template Te>
void BFS(int v, int clock){
Queue<int> Q;
status(v) = DISCOVERED;
Q.enqueue(v); // 最初节点入列
while(!Q.empty()){
v = Q.dequeue; dTime(v) = ++clock;
int u = firstNbr(v);
while(u > -1){
Q.enqueue(u); // 将与v相连的节点入列,并更新节点的状态信息
status(u) = DISCOVERED;
partent(u) = v;
u = nextNbr(v, u); // 更新相连节点
}
}
}
对于clock的解释,我们会在后面统一解释
DFS:深度优先搜索:策略:以一个节点开始,沿一条线深入遍历,当到终点时,回到上一个节点,遍历其他兄弟节点,很像二叉树的后序遍历。
举个例子:一个人走迷宫,从起点开始,随便选一条路深入,在每个岔路口都随机选一条路深入,当遇到死胡同时,向后退,到上一个岔路口,选下一条路。
例子:求二叉树的深度,对每一个节点,可使用DFS思想。
实现:以迭代的形式实现,引入一个辅助栈。
代码如下:与上面的BFS十分相似
template<typename Tv, typename Te>
void dfs(int start){
reset();
int v = start;
int clock = 0;
do{
if(UNDISCOVERED == status(v))
DFS(v, clock);
}while(s != (v = (++v % n)))
}
template<typename Tv, typename Te>
void DFS(int v, int clock){
Stack<int> S;
status(v) = DISCOVERED;
S.push(v);
while(!S.empty()){
v = S.pop();
for(int u = firstNbr(v); u > -1; u = nextNbr(v, u)){
status(u) = DISCOVERED; dTime(u) = clock++;
S.push(u); partent(u) = v;
}
}
}
比较下,DFS与BFS实现是相同的,只不过一个用的是栈,一个用的是队列。
在本章中,对于参数,只修改了状态,至于边,可自行实现下。