跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

深度优先搜索 (DFS) 算法原理与 C++ 实现

综述由AI生成深度优先搜索 (DFS) 算法的核心原理,包括“不撞南墙不回头”的思想、三色标记法及递归与栈的关系。提供了 C++ 递归与迭代版本代码,并展示了连通分量检测、二叉树路径总和及全排列生成等实用场景。内容涵盖算法步骤、复杂度分析及注意事项,帮助读者掌握 DFS 在图论与回溯问题中的应用。

不知所云发布于 2026/3/23更新于 2026/5/3020K 浏览

1. 深度优先搜索 (DFS) 核心原理

1.1 核心思想:一条路走到黑

DFS 的核心思想可以用一句俗语概括:'不撞南墙不回头'。它从起点出发,沿着一条路径尽可能深地探索,直到无法继续(没有未访问的邻接点或到达目标),然后回溯到上一个分支点,选择另一条未走过的路径继续探索,直到遍历完所有可达节点。

形象比喻:你在迷宫中,随便选一个方向,一直往前走,遇到死胡同就退回到上一个岔路口,换一个方向继续走。这个过程就是 DFS。

1.2 工作过程详解

我们通过一个简单的树形结构来可视化 DFS 的工作过程(节点访问顺序用数字标记):

 A(1)
/ \
B(2) E(5)
/ \ \
C(3) D(4) F(6)

从 A 出发的 DFS 遍历顺序为:A → B → C → D → E → F 具体步骤:

  1. 访问 A,标记已访问。
  2. 从 A 的第一个邻接点 B 出发,访问 B,标记。
  3. 从 B 的第一个邻接点 C 出发,访问 C,标记。
  4. C 无未访问邻接点,回溯到 B。
  5. 从 B 的下一个邻接点 D 出发,访问 D,标记。
  6. D 无未访问邻接点,回溯到 B;B 也无其他邻接点,回溯到 A。
  7. 从 A 的下一个邻接点 E 出发,访问 E,标记。
  8. 从 E 的第一个邻接点 F 出发,访问 F,标记。
  9. F 无未访问邻接点,回溯到 E;E 无其他邻接点,回溯到 A。
  10. A 所有邻接点访问完毕,结束。

1.3 三色标记法

在 DFS 执行过程中,常用三种颜色标记节点状态,这有助于理解递归栈和回溯:

  • 白色:尚未发现的节点(未被访问)。
  • 灰色:已发现但尚未处理完所有邻接点的节点(当前在递归栈中)。
  • 黑色:所有邻接点都已处理完毕的节点(已完成探索)。

这个标记法在环检测、拓扑排序等高级应用中至关重要。

1.4 算法步骤(递归版本)

DFS 通常用递归实现,天然契合'深入 - 回溯'的过程:

void DFS(int u) {
    // 标记 u 为已访问(白色 → 灰色)
    visited[u] = true;
    for (int v : adj[u]) {
        if (!visited[v]) {
            DFS(v);
        }
    }
    // 标记 u 为完全处理(灰色 → 黑色)
}

1.5 递归与栈的关系

递归的本质是系统栈。每次递归调用都会将当前状态(节点、局部变量)压入系统栈,回溯时从栈顶弹出。因此,DFS 也可以显式使用栈来实现非递归版本,避免递归深度限制。

// 显式栈模拟 DFS
stack<int> stk;
stk.push(start);
while (!stk.empty()) {
    int cur = stk.top();
    stk.pop();
    if (!visited[cur]) {
        visited[cur] = true;
        for (int nei : adj[cur]) {
            if (!visited[nei]) {
                stk.push(nei);
            }
        }
    }
}

1.6 关键特性总结

特性说明
遍历顺序沿着深度优先,可能先深后广
空间复杂度O(h),h 为递归深度(树高)
时间复杂度O(V+E),每个节点和边访问一次
是否保证最短路径❌ 不保证
适用场景连通性检测、回溯问题、拓扑排序、路径存在性

2. 深度优先搜索 (DFS) 动画演示理解

2.1 动画演示理解

通过动态演示可以更直观地理解 DFS 的遍历过程,包含二叉树和有向图的示例。动图展示了节点被访问的顺序以及栈的变化情况。

3. 基本演示代码和实用兼容代码

3.1 基本代码演示

C++ 递归:

void dfs_recursive(const vector<vector<int>>& graph, int node, unordered_set<int>& visited) {
    visited.insert(node);
    cout << node << " "; // 访问节点
    for (int neighbor : graph[node]) {
        if (visited.find(neighbor) == visited.end()) {
            dfs_recursive(graph, neighbor, visited);
        }
    }
}

C++ 栈版本:

void dfs_iterative(const vector<vector<int>>& graph, int start) {
    unordered_set<int> visited;
    stack<int> stk;
    stk.push(start);
    while (!stk.empty()) {
        int node = stk.top();
        stk.pop();
        if (visited.find(node) == visited.end()) {
            visited.insert(node);
            cout << node << " "; // 将邻居逆序压栈,使访问顺序与递归相近
            const auto& neighbors = graph[node];
            for (auto it = neighbors.rbegin(); it != neighbors.rend(); ++it) {
                if (visited.find(*it) == visited.end()) {
                    stk.push(*it);
                }
            }
        }
    }
}

C++ 二叉树遍历:

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

void dfs_tree(TreeNode* root) {
    if (!root) return;
    cout << root->val << " "; // 前序
    dfs_tree(root->left);
    dfs_tree(root->right);
}

3.2 实用兼容代码

1. 连通分量检测(无向图)

应用场景:社交网络中的朋友圈划分、图像连通域标记等。

#include <iostream>
#include <vector>
using namespace std;

void dfs(int node, vector<vector<int>>& graph, vector<bool>& visited, vector<int>& component) {
    visited[node] = true;
    component.push_back(node);
    for (int neighbor : graph[node]) {
        if (!visited[neighbor]) dfs(neighbor, graph, visited, component);
    }
}

vector<vector<int>> findConnectedComponents(vector<vector<int>>& graph, int n) {
    vector<bool> visited(n, false);
    vector<vector<int>> components;
    for (int i = 0; i < n; ++i) {
        if (!visited[i]) {
            vector<int> comp;
            dfs(i, graph, visited, comp);
            components.push_back(comp);
        }
    }
    return components;
}

int main() {
    // 图:0-1-2 3-4
    vector<vector<int>> graph = {{1}, {0,2}, {1}, {4}, {3}, {}};
    int n = 6;
    auto comps = findConnectedComponents(graph, n);
    cout << "连通分量:" << endl;
    for (auto& comp : comps) {
        for (int x : comp) cout << x << " ";
        cout << endl;
    }
    return 0;
}
2. 二叉树路径总和 II(返回所有满足条件的路径)

应用场景:路径规划、决策树回溯(如求所有和为某值的路径)。

#include <iostream>
#include <vector>
using namespace std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode* l, TreeNode* r) : val(x), left(l), right(r) {}
};

void dfs(TreeNode* node, int currentSum, int targetSum, vector<int>& path, vector<vector<int>>& res) {
    if (!node) return;
    currentSum += node->val;
    path.push_back(node->val);
    if (!node->left && !node->right && currentSum == targetSum) {
        res.push_back(path);
    } else {
        dfs(node->left, currentSum, targetSum, path, res);
        dfs(node->right, currentSum, targetSum, path, res);
    }
    path.pop_back();
}

vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
    vector<vector<int>> res;
    vector<int> path;
    dfs(root, 0, targetSum, path, res);
    return res;
}

int main() {
    // 构建树
    TreeNode* root = new TreeNode(5);
    root->left = new TreeNode(4, new TreeNode(11, new TreeNode(7), new TreeNode(2)), nullptr);
    root->right = new TreeNode(8, new TreeNode(13), new TreeNode(4, nullptr, new TreeNode(1)));
    int target = 22;
    auto paths = pathSum(root, target);
    cout << "路径总和为 " << target << " 的路径:" << endl;
    for (auto& p : paths) {
        for (int v : p) cout << v << " ";
        cout << endl;
    }
    return 0;
}
3. 全排列生成(回溯法)

应用场景:密码破解、排列组合枚举、旅行商问题初始排列。

#include <iostream>
#include <vector>
using namespace std;

void dfs(vector<int>& nums, vector<bool>& used, vector<int>& path, vector<vector<int>>& res) {
    if (path.size() == nums.size()) {
        res.push_back(path);
        return;
    }
    for (int i = 0; i < nums.size(); ++i) {
        if (!used[i]) {
            used[i] = true;
            path.push_back(nums[i]);
            dfs(nums, used, path, res);
            path.pop_back();
            used[i] = false;
        }
    }
}

vector<vector<int>> permute(vector<int>& nums) {
    vector<vector<int>> res;
    vector<int> path;
    vector<bool> used(nums.size(), false);
    dfs(nums, used, path, res);
    return res;
}

int main() {
    vector<int> nums = {1,2,3};
    auto perms = permute(nums);
    cout << "全排列:" << endl;
    for (auto& p : perms) {
        for (int v : p) cout << v << " ";
        cout << endl;
    }
    return 0;
}

4. 注意事项

误区正确做法
忘记标记 visited进入 DFS 前立即标记,防止重复访问
回溯时忘记撤销如果在递归前修改了状态(如 path),递归后一定要还原
递归深度过大导致栈溢出改用迭代栈,或设置递归深度限制
在无向图中遍历所有节点主函数要遍历每个节点,对未访问的调用 DFS(处理非连通图)

目录

  1. 1. 深度优先搜索 (DFS) 核心原理
  2. 1.1 核心思想:一条路走到黑
  3. 1.2 工作过程详解
  4. 1.3 三色标记法
  5. 1.4 算法步骤(递归版本)
  6. 1.5 递归与栈的关系
  7. 1.6 关键特性总结
  8. 2. 深度优先搜索 (DFS) 动画演示理解
  9. 2.1 动画演示理解
  10. 3. 基本演示代码和实用兼容代码
  11. 3.1 基本代码演示
  12. 3.2 实用兼容代码
  13. 1. 连通分量检测(无向图)
  14. 2. 二叉树路径总和 II(返回所有满足条件的路径)
  15. 3. 全排列生成(回溯法)
  16. 4. 注意事项
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • VS Code 远程连接服务器后 GitHub Copilot 无法使用的修复方法
  • 基于 Spring Cloud 的分布式智能推荐系统实现
  • AI 论文生成工具:一字成文核心功能解析
  • OpenDroneMap 无人机影像三维建模安装与实战指南
  • Rust WebAssembly 与 Three.js 结合的高性能 3D 粒子系统实战
  • Java 核心技术面试题精选与解析
  • 基于 AI 工具快速生成计算机课题技术路线图
  • Vue Vant van-uploader 文件上传接口封装方法
  • Mac 下使用 Docker 部署 FastGPT 构建 AI 私有知识库
  • Python NumPy 入门:数据处理与科学计算基础
  • Paperzz 降重与 AIGC 检测功能实测分析
  • OpenClaw 在 Linux/Ubuntu 上的安装与部署实战
  • AutoFigure:从长文本自动生成高质量科研插图
  • HarmonyOS ArkUI Toolbar 组件通用属性与实战用法
  • 豆包 Seedream 4.0 多图融合能力测评:田园犬与三花猫多场景创作
  • 数据结构:栈与队列的底层实现
  • 2024 版 Kali Linux 虚拟机安装教程(详细步骤)
  • 植物大战僵尸融合版多平台安装与常见问题解决指南
  • Coze 打造专属 AI 应用:从智能体开发到 Web 部署
  • 纯 C# 自研轻量 UI 引擎 XchyUI,内核小于 200KB 支持跨平台

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online