深度优先搜索算法

深度优先搜索算法是一种用于图形和树的遍历算法。它通过沿着树的深度(而不是广度)努力地搜索最佳解来实现,即找到最优解,或者覆盖树中的所有节点。算法的主要特征是它以一种比较深的方式去搜索问题的解空间,而不是从上到下进行搜索。

算法流程

树的每个节点都被记录下来,作为搜索的初始节点,从第一个节点开始,搜索它的子节点直到子节点没有子节点,然后回到父节点,搜索它的兄弟节点,如此反复遍历搜索整棵树,直到没有叶节点,它是一种递归算法。

时间复杂度

深度优先搜索的时间复杂度是指数级别的,它和节点数量成正比,比如节点数为N,则时间复杂度O(N)。

空间复杂度

由于深度优先搜索算法用栈来存储搜索路径,而栈的深度和节点的数量相同,因此空间复杂度也是O(N),和时间复杂度同样。

优点和缺点

深度优先搜索的优点是它能够搜索出一个最优解,并且实现很简单,它的缺点就是它非常容易进入死胡同,找不到最优解,另外时间和空间复杂度也是较高的。

示例代码

Python

def dfs(node, visited): 
    # 将节点加入到访问队列中 
    visited.add(node) 
    
    # 遍历其子节点 
    for n in node.children: 
        if n not in visited: 
            dfs(n, visited)

Java

使用Java来实现深度优先搜索算法,可以用下面的代码:

import java.util.LinkedList;
import java.util.Queue;

public class DFS {
    private int V; // 节点数
    private LinkedList<Integer> adj[]; // 邻接表

    // 构造函数,初始化图
    public DFS(int v) {
        V = v;
        adj = new LinkedList[v];
        for (int i = 0; i < v; i++)
            adj[i] = new LinkedList<>();
    }

    // 向图中添加边
    public void addEdge(int v, int w) {
        adj[v].add(w);
    }

    // 深度优先搜索实现
    public void DFSUtil(int v, boolean visited[]) {
        // 标记当前节点为已访问
        visited[v] = true;
        System.out.print(v + " ");

        // 访问与v相连的所有节点
        for (int i : adj[v]) {
            if (!visited[i])
                DFSUtil(i, visited);
        }
    }

    // 调用DFSUtil函数
    public void DFS(int v) {
        // 默认所有节点都未访问
        boolean visited[] = new boolean[V];

        // 调用深度优先搜索实现
        DFSUtil(v, visited);
    }

    public static void main(String args[]) {
        DFS g = new DFS(4);

        g.addEdge(0, 1);
        g.addEdge(0, 2);
        g.addEdge(1, 2);
        g.addEdge(2, 0);
        g.addEdge(2, 3);
        g.addEdge(3, 3);

        System.out.println("从节点 2 开始深度优先搜索: ");
        g.DFS(2);
    }
}

应用场景

深度优先搜索算法在图论中得到了广泛的应用,如拓扑排序、连通性检测,也可用于解决最短路径问题,以及解决迷宫问题,寻找旅行商问题等。

正面例子

深度优先搜索算法可以用来在节点间找到最短路径,也可以用它求解NP-hard问题,比如求解最小生成树(MST)。

反面例子

深度优先搜索算法容易落入死胡同,如果树中有大量的节点,那么时间和空间复杂度都会很高,而且非常容易导致内存溢出。