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