深度优先搜索(DFS)简介
深度优先搜索(DFS,Depth-First Search) 是一种用于遍历或搜索图或树的算法。DFS 从一个节点出发,沿着图的一个分支不断向下搜索,直到没有可以继续深入的节点为止。然后,它会回溯到上一个节点,继续搜索其他未访问的邻居。DFS 适合用于搜索问题、路径寻找、连通性检查等场景。
DFS 的特点是 递归(或者使用栈) 实现,并且在搜索过程中 尽可能深入 图的每一条分支,直到无法继续下去,然后回溯继续探索其他分支。
深度优先搜索的基本思路
- 从起始节点开始,访问该节点。
- 标记当前节点为已访问。
- 对于每个未被访问的邻居,递归地执行 DFS。
- 当所有邻居都被访问后,回溯到上一节点,继续探索其他未访问的邻居。
DFS 算法的实现
1. 递归实现 DFS
递归方式是 DFS 最常见的实现方法,它通过系统的调用栈自动管理回溯过程。
2. 非递归实现 DFS
通过显式使用栈数据结构来模拟递归过程,手动管理节点的访问顺序。
示例
假设我们有一个无向图,图通过邻接表(adjacency list)来表示。例如,图可以用一个对象表示,节点是键,值是与该节点相邻的节点数组。
// 定义图的结构(邻接表)
const graph = {
0: [1, 2],
1: [0, 3, 4],
2: [0, 4],
3: [1],
4: [1, 2],
};
1. 递归实现 DFS
递归版本的 DFS 会利用栈(调用栈)来追踪路径,直到所有节点都被访问过。
代码实现
// 深度优先搜索(递归实现)
function dfsRecursive(graph, node, visited = new Set()) {
// 标记当前节点为已访问
visited.add(node);
console.log(node); // 访问当前节点
// 遍历该节点的所有邻居节点
for (let neighbor of graph[node]) {
if (!visited.has(neighbor)) {
// 如果邻居未被访问,递归访问
dfsRecursive(graph, neighbor, visited);
}
}
}
// 示例使用
dfsRecursive(graph, 0); // 从节点0开始DFS
输出结果:
0
1
3
4
2
2. 非递归实现 DFS
非递归实现 DFS 可以通过显式使用栈来模拟递归的过程,手动管理节点的访问顺序。
代码实现
// 深度优先搜索(非递归实现)
function dfsIterative(graph, start) {
const visited = new Set(); // 记录已访问的节点
const stack = [start]; // 用栈来存储待访问的节点
while (stack.length > 0) {
// 从栈中弹出一个节点
const node = stack.pop();
if (!visited.has(node)) {
visited.add(node); // 标记该节点为已访问
console.log(node); // 访问当前节点
// 将该节点的未访问邻居节点压入栈
for (let neighbor of graph[node]) {
if (!visited.has(neighbor)) {
stack.push(neighbor);
}
}
}
}
}
// 示例使用
dfsIterative(graph, 0); // 从节点0开始DFS
输出结果:
0
2
4
1
3
比较递归和非递归实现
- 递归实现:更简洁,利用系统的调用栈来处理回溯。
- 非递归实现:通过手动管理栈来模拟递归,适用于栈深度较大的情况,避免递归深度过深导致栈溢出。
总结
- DFS 是图和树的基本遍历算法,常用递归或栈实现。
- 递归实现更简洁,但可能存在栈溢出问题,尤其是在大规模图中。
- 非递归实现使用栈显式地模拟递归过程,适合大图遍历。
- DFS 的应用包括图的连通性、路径查找、环检测等。
通过 DFS,你可以高效地访问和处理图结构的节点,解决许多图相关的问题。