算法介绍:
深度优先搜索(Depth-First Search,简称DFS)是一种用于遍历或搜索树或图的算法。它沿着图的分支尽可能深地探索,当到达某个分支的末梢时回溯,再探索下一个分支,直到所有的节点都被访问。DFS强调“深入探索”,优先探索一条路径直到无法继续,再回溯至前一节点探索其他路径。
实现原理:
- 定义一个DFS函数,接收三个参数:起始节点
i
,邻接矩阵G
和标记数组visited
。 - 在主函数中,定义了一个邻接矩阵
G
和一个标记数组visited
。 - 调用DFS函数,传入起始节点
2
,邻接矩阵G
和标记数组visited
。 - 在DFS函数中,首先将起始节点标记为已访问,并打印。然后,遍历邻接矩阵
G
中与起始节点相邻的节点,如果该节点没有被访问过且在邻接矩阵中与起始节点有连接(即G[i][j] == 1
),则递归调用DFS函数。 - 注意:由于递归的缘故,不能将标记数组
visited
定义在DFS函数体内。在主函数中定义了一个标记数组,并在调用DFS函数时传入。
特点:
- 深度探索:DFS优先沿着一条路径深入,直到到达叶子节点或无法继续深入为止。
- 栈或递归:自然适合用栈来实现(后进先出LIFO性质),也可以通过递归函数调用来实现。
- 路径追踪:在某些应用中,DFS可以用来追踪从起始节点到当前节点的路径。
- 循环检测:在有向图中,DFS可以用来检测环的存在。
- 时间与空间复杂度:在无向图中,DFS的时间复杂度为O(V+E),空间复杂度在最坏情况下也是O(V+E),但若使用递归则受限于递归深度,可能会导致栈溢出。
应用场景:
- 路径查找与环检测:在图中查找是否存在一条路径到达目标节点或检测图中是否有环。
- 连通分量:确定图的连通性,找出所有强连通分量。
- 拓扑排序辅助:虽然拓扑排序主要依赖BFS,但在某些步骤中DFS用于确定每个节点的结束时间,从而辅助进行拓扑排序。
- 剪枝与回溯问题:DFS是解决许多回溯问题的基础,如八皇后问题、迷宫寻路等,通过剪枝减少不必要的探索。
实现注意事项:
- 为了避免无限循环和重复访问,需要维护一个数据结构(如布尔数组)来记录已访问的节点。
- 递归实现时需要注意递归深度限制,对于极深的图或树结构,可能需要考虑非递归实现方式。
以下为完整程序代码(包含测试用例):
//深度优先遍历
//邻接矩阵
public class DFS_2 {
// DFS函数
// i 起始结点
// G 邻接矩阵
// visited 标记数组
public static void dfs(int i, int[][] G, boolean[] visited) {
System.out.print(i);// 访问初始结点i
visited[i] = true;// 将初始结点标记为已访问
for (int j = 0; j < G.length; j++) {
if (visited[j] == false && G[i][j] == 1) {
dfs(j, G, visited);// 递归
}
}
}
public static void main(String[] args) {
int[][] G = {
{ 0, 1, 1, 0, 1 },
{ 1, 0, 0, 1, 1 },
{ 1, 0, 0, 0, 1 },
{ 0, 1, 0, 0, 1 },
{ 1, 1, 1, 1, 0 }
};// 邻接矩阵
boolean[] visited = new boolean[G.length];// 标记数组 注意:由于递归的缘故,不能将标记数组定义在dfs函数体内
dfs(2, G, visited);
}
}
测试结果如下: