DFS 的核心思想
DFS 是一种用递归或栈来实现的搜索算法。其主要特点是尽可能深地访问节点,然后再回溯。
适用场景:
- 图遍历 (Graph Traversal)
- 树遍历 (Tree Traversal)
- 连通性判断
- 迷宫路径搜索等
DFS 的两种实现方式
- 递归实现:借助函数调用栈。
- 非递归实现:使用显式栈模拟递归。
DFS 递归实现(图的遍历)
假设我们有一个图,用邻接表表示,我们需要从某个起点开始深度优先搜索整个图。
示例代码:
import java.util.*;
public class DFSExample {
// 邻接表表示图
private static Map<Integer, List<Integer>> graph = new HashMap<>();
// 访问标记数组
private static boolean[] visited;
public static void main(String[] args) {
// 构造图
graph.put(0, Arrays.asList(1, 2));
graph.put(1, Arrays.asList(0, 3, 4));
graph.put(2, Arrays.asList(0, 5, 6));
graph.put(3, Arrays.asList(1));
graph.put(4, Arrays.asList(1));
graph.put(5, Arrays.asList(2));
graph.put(6, Arrays.asList(2));
// 节点数(图的大小)
int n = 7;
visited = new boolean[n];
// 从节点 0 开始 DFS
System.out.println("DFS 遍历顺序:");
dfs(0);
}
// DFS 方法
public static void dfs(int node) {
// 标记当前节点已访问
visited[node] = true;
System.out.print(node + " ");
// 遍历与当前节点相邻的节点
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
dfs(neighbor); // 递归调用
}
}
}
}
运行结果:
DFS 遍历顺序:
0 1 3 4 2 5 6
DFS 非递归实现(使用显式栈)
如果我们不想用递归,可以使用一个栈来模拟递归过程:
示例代码:
import java.util.*;
public class DFSNonRecursive {
private static Map<Integer, List<Integer>> graph = new HashMap<>();
private static boolean[] visited;
public static void main(String[] args) {
// 构造图
graph.put(0, Arrays.asList(1, 2));
graph.put(1, Arrays.asList(0, 3, 4));
graph.put(2, Arrays.asList(0, 5, 6));
graph.put(3, Arrays.asList(1));
graph.put(4, Arrays.asList(1));
graph.put(5, Arrays.asList(2));
graph.put(6, Arrays.asList(2));
// 节点数
int n = 7;
visited = new boolean[n];
// 从节点 0 开始 DFS
System.out.println("DFS 遍历顺序:");
dfs(0);
}
public static void dfs(int start) {
Stack<Integer> stack = new Stack<>();
stack.push(start);
while (!stack.isEmpty()) {
int node = stack.pop();
if (!visited[node]) {
// 标记节点已访问
visited[node] = true;
System.out.print(node + " ");
// 将相邻节点压入栈(注意顺序以保持一致)
for (int neighbor : graph.get(node)) {
if (!visited[neighbor]) {
stack.push(neighbor);
}
}
}
}
}
}
运行结果:
DFS 遍历顺序:
0 2 6 5 1 4 3
DFS 示例问题
题目:判断一个二维迷宫中是否存在从起点到终点的路径。
迷宫示例:
迷宫是一个二维数组:
1 0 0 0
1 1 0 1
0 1 0 0
1 1 1 1
1
表示可通行路径0
表示墙壁- 起点在
(0, 0)
,终点在(3, 3)
示例代码:
public class MazeDFS {
private static int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 方向数组
public static void main(String[] args) {
int[][] maze = {
{1, 0, 0, 0},
{1, 1, 0, 1},
{0, 1, 0, 0},
{1, 1, 1, 1}
};
System.out.println("是否存在路径: " + hasPath(maze, 0, 0, 3, 3));
}
public static boolean hasPath(int[][] maze, int startX, int startY, int endX, int endY) {
int rows = maze.length, cols = maze[0].length;
boolean[][] visited = new boolean[rows][cols];
return dfs(maze, startX, startY, endX, endY, visited);
}
private static boolean dfs(int[][] maze, int x, int y, int endX, int endY, boolean[][] visited) {
// 判断是否越界或已经访问或不可通行
if (x < 0 || y < 0 || x >= maze.length || y >= maze[0].length || visited[x][y] || maze[x][y] == 0) {
return false;
}
// 如果到达终点,返回 true
if (x == endX && y == endY) {
return true;
}
// 标记当前位置已访问
visited[x][y] = true;
// 尝试四个方向移动
for (int[] direction : directions) {
int newX = x + direction[0];
int newY = y + direction[1];
if (dfs(maze, newX, newY, endX, endY, visited)) {
return true;
}
}
// 回溯时取消标记
visited[x][y] = false;
return false;
}
}
运行结果:
是否存在路径: true