图论算法之DFS与BFS

时间:2023-03-09 14:18:23
图论算法之DFS与BFS
  • 概述(总)
DFS是算法中图论部分中最基本的算法之一。对于算法入门者而言,这是一个必须掌握的基本算法。它的算法思想可以运用在很多地方,利用它可以解决很多实际问题,但是深入掌握其原理是我们灵活运用它的关键所在。
  • 含义特点
DFS即深度优先搜索,有点类似广度优先搜索,也是对一个连通图进行遍历的算法。它的思想是从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底,这种尽量往深处走的概念即是深度优先的概念。
由于用到递归,当节点特别多且深度很大的时候,可能会发生栈溢出。解决办法是将递归改为非递归,自行编写栈。
BFS即广度优先搜索(也称宽度优先搜索),是连通图的一种遍历策略。因为它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域,故得名。 一般可以用它做什么呢?一个最直观经典的例子就是走迷宫,我们从起点开始,找出到终点的最短路程,很多最短路径算法就是基于广度优先的思想成立的。
  • 应用场景
  • dfs
  1. 连通分量
连通分量:任意两点可互达的图。
求无向图的连通分量:O(n)
  1. 二分图判定
二分图:每条边的两个节点分别着不同的两种颜色。
判断一个图是否是二分图:O(n)
  1. 二叉树的递归遍历
  • bfs
  1. 求割顶和桥
割顶:对于无向图G,如果删除某点u后,连通分量数目增加,则u即割顶。
桥:对于无向图G,如果删除某点边e后,连通分量数目增加,则e即桥。
求所有的图中的割顶和桥:
方式一:尝试删除每个节点,用dfs判断连通分量是否增加。时间复杂度:O(n(n+m))。
方式二:给每个节点记录两个时间戳,深入挖掘dfs。时间复杂度:O(n+m)。

  2.二叉树的层次遍历

  • 代码实现
/**
* DFS核心伪代码
* 前置条件是visit数组全部设置成false
* @param n 当前开始搜索的节点
* @param d 当前到达的深度
* @return 是否有解
*/
bool DFS(Node n, int d){
if (isEnd(n, d)){//一旦搜索深度到达一个结束状态,就返回true
return true;
}
for (Node nextNode in n){//遍历n相邻的节点nextNode
if (!visit[nextNode]){//
visit[nextNode] = true;//在下一步搜索中,nextNode不能再次出现
if (DFS(nextNode, d+1)){//如果搜索出有解
//做些其他事情,例如记录结果深度等
return true;
}
//重新设置成false,因为它有可能出现在下一次搜索的别的路径中
visit[nextNode] = false;
}
}
return false;//本次搜索无解
}
/**
* 广度优先搜索
* @param Vs 起点
* @param Vd 终点
*/
bool BFS(Node& Vs, Node& Vd){
queue<Node> Q;
Node Vn, Vw;
int i; //初始状态将起点放进队列Q
Q.push(Vs);
hash(Vw) = true;//设置节点已经访问过了! while (!Q.empty()){//队列不为空,继续搜索!
//取出队列的头Vn
Vn = Q.front(); //从队列中移除
Q.pop(); while(Vw = Vn通过某规则能够到达的节点){
if (Vw == Vd){//找到终点了!
//把路径记录,这里没给出解法
return true;//返回
} if (isValid(Vw) && !visit[Vw]){
//Vw是一个合法的节点并且为白色节点
Q.push(Vw);//加入队列Q
hash(Vw) = true;//设置节点颜色
}
}
}
return false;//无解
}
  • 总结(总)
  • DFS适合此类题目:给定初始状态跟目标状态,要求判断从初始状态到目标状态是否有解。
  • BFS适合此类题目:给定初始状态跟目标状态,要求从初始状态到目标状态的最短路径。
  • 参考资料