引言

深度优先搜索(DFS)是一种在树或图中进行遍历的算法,它通过沿着一条路径深入探索,直到这条路径的终点,然后回溯并探索其他路径。DFS在算法设计中应用广泛,尤其在解决路径发现、拓扑排序、谜题解决、连通性检测和迷宫生成等问题中表现出色。本文将深入解析DFS的核心例题,并提供实战技巧。

一、DFS基础概念

深度优先搜索(DFS)的核心思想是尽可能深地搜索树的分支,直到达到叶子节点,然后回溯到前一个节点,继续搜索其他分支。

1.1 递归实现

递归实现DFS是最直观的方式,通过递归调用自身来探索所有可能的路径。

void dfsRecursive(TreeNode* node) {
    if (node == nullptr) return;
    // 处理当前节点
    dfsRecursive(node->left);  // 探索左子树
    dfsRecursive(node->right); // 探索右子树
}

1.2 迭代实现

迭代实现DFS通常使用栈来模拟递归的过程。

void dfsIterative(TreeNode* root) {
    stack<TreeNode*> stk;
    stk.push(root);
    while (!stk.empty()) {
        TreeNode* node = stk.top();
        stk.pop();
        // 处理当前节点
        if (node->right) stk.push(node->right);
        if (node->left) stk.push(node->left);
    }
}

二、DFS核心例题解析

2.1 迷宫问题

给定一个二维数组,其中0表示可以走的路径,1表示障碍物,找出从起点到终点的路径。

bool dfsMaze(vector<vector<int>>& maze, vector<int>& path, int x, int y) {
    if (x < 0 || x >= maze.size() || y < 0 || y >= maze[0].size() || maze[x][y] == 1) return false;
    if (x == maze.size() - 1 && y == maze[0].size() - 1) {
        path.push_back({x, y});
        return true;
    }
    path.push_back({x, y});
    maze[x][y] = 1; // 标记为已访问
    if (dfsMaze(maze, path, x + 1, y) || dfsMaze(maze, path, x - 1, y) ||
        dfsMaze(maze, path, x, y + 1) || dfsMaze(maze, path, x, y - 1)) {
        return true;
    }
    path.pop_back();
    maze[x][y] = 0; // 回溯
    return false;
}

2.2 岛屿问题

给定一个二维数组,其中0表示水,1表示陆地,计算岛屿的数量。

int dfsIslands(vector<vector<int>>& grid, int i, int j) {
    if (i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size() || grid[i][j] == 0) return 0;
    grid[i][j] = 0; // 标记为已访问
    return 1 + dfsIslands(grid, i + 1, j) + dfsIslands(grid, i - 1, j) +
           dfsIslands(grid, i, j + 1) + dfsIslands(grid, i, j - 1);
}

2.3 朋友圈问题

给定一个用户关系列表,其中每个元素是一个用户,用户之间的关系以列表的形式给出,找出所有好友圈。

vector<vector<int>> dfsFriendCircles(vector<vector<int>>& M) {
    vector<vector<int>> result;
    vector<bool> visited(M.size(), false);
    for (int i = 0; i < M.size(); ++i) {
        if (!visited[i]) {
            vector<int> circle;
            dfs(M, visited, circle, i);
            result.push_back(circle);
        }
    }
    return result;
}

void dfs(vector<vector<int>>& M, vector<bool>& visited, vector<int>& circle, int i) {
    visited[i] = true;
    for (int j = 0; j < M[i].size(); ++j) {
        if (M[i][j] == 1 && !visited[j]) {
            circle.push_back(j);
            dfs(M, visited, circle, j);
        }
    }
}

三、实战技巧

3.1 空间优化

在DFS中,递归可能会导致大量的函数调用栈,导致空间复杂度过高。可以通过迭代实现或使用尾递归优化来减少空间复杂度。

3.2 时间优化

对于包含大量重复子问题的DFS问题,可以使用记忆化搜索来避免重复计算,提高算法效率。

3.3 注意边界条件

在实现DFS时,要特别注意边界条件,如数组越界、空指针等,以避免程序出错。

3.4 逻辑清晰

在编写DFS代码时,要确保逻辑清晰,便于理解和维护。

总结

深度优先搜索(DFS)是一种强大的算法,在解决树和图相关问题时具有广泛的应用。通过深入理解DFS的基础概念、核心例题和实战技巧,我们可以更好地运用DFS解决实际问题。