火龙信奥
  • 分享
  • 课程
  • 在线题库
  • CSES
    • GESP
    • CSP
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 知识课堂
  • 在线比赛
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

搜索与图论模块讲义

作者: 作者的头像   huolong , 时间:2026-09-25 15:12:25 , 所有人可见, 阅读  36

搜索与图论模块讲义

本模块包含 DFS、BFS、树与图的深度优先遍历、树与图的广度优先遍历、拓扑排序、Dijkstra、Bellman-Ford、SPFA、Floyd、Prim、Kruskal、染色法判定二分图、匈牙利算法 共 13 个核心图论与搜索算法。


1. DFS (深度优先搜索 - Depth First Search)

核心思想与性格

  • 性格:不撞南墙不回头。
  • 做法:一条路走到黑。先选一条路一直往前走,走不通了(死胡同)就退回上一步(这叫回溯),换一条路再试,直到把所有的可能性全部搜完。通常使用递归实现。

经典应用:全排列问题

用 DFS 生成 $1 \sim n$ 的全排列:

int path[N], st[N]; // path存当前路径,st记录哪个数字被用过了
void dfs(int u) {
    if (u > n) {
        for (int i = 1; i <= n; i++) cout << path[i] << " ";
        cout << endl;
        return;
    }
    for (int i = 1; i <= n; i++) {
        if (!st[i]) {
            st[i] = true; path[u] = i;
            dfs(u + 1);
            st[i] = false; // 回溯,恢复现场
        }
    }
}

2. BFS (广度优先搜索 - Breadth First Search)

核心思想与性格

  • 性格:稳扎稳打,层层推进。
  • 做法:像水波纹一样向四周一圈一圈扩散。先看离起点距离为 1 的所有点,再看距离为 2 的所有点……
  • 特点:通常使用队列 (Queue) 实现。在边权全部相等(或无权图)的情况下,BFS 搜索到目标时,走过的步数一定是最短路。

3. 树与图的深度优先遍历 & 4. 树与图的广度优先遍历

图的存储:邻接表

在计算机里,我们用邻接表来存图。每一个节点都有一个链表,记录它能直接走到哪些邻居。

// h[N] 存每个点链表的头节点,e[] 存邻居是谁,ne[] 存下一条边
int h[N], e[M], ne[M], idx;

void add(int a, int b) { // 添加一条从 a 到 b 的有向边
    e[idx] = b; ne[idx] = h[a]; h[a] = idx++;
}
  • 树的 DFS:递归遍历每个节点的子树,常用于求树的深度、子树节点个数。
  • 树的 BFS:利用队列进行层序遍历,常用于求树的宽度或按层处理信息。

5. 拓扑排序 (Topological Sort)

核心思想

  • 场景:学校选课。想学《高等数学》必须先学《初等数学》。
  • 拓扑排序:把一张有向无环图(DAG)的所有节点排成一个线性序列,使得对图中的每一条有向边 $(u, v)$,$u$ 都排在 $v$ 的前面。
  • 入度:有几条边指向这个点。

算法步骤

  1. 把所有入度为 0 的点(没有前置要求)放入队列。
  2. 队头元素出队,把它指向的邻居的入度减 1。
  3. 如果邻居的入度减到了 0,立刻把这个邻居也丢进队列。
  4. 重复直到队列为空。如果最后所有点都被遍历到了,说明存在拓扑排序(图无环)。

6. Dijkstra 算法

作用与限制

  • 作用:求单源最短路径(从一个起点到其他所有点的最短距离)。
  • 核心限制:图里面绝对不能有负权边(否则贪心会失效)。
  • 时间复杂度:朴素版 $O(n^2)$,堆优化版 $O(m \log n)$。

贪心策略

每次从未确定最短路的点中,挑一个离起点最近的点,把它标记为“已确定”,然后用它去更新它所有邻居的距离(这叫松弛操作)。

// 堆优化 Dijkstra 核心逻辑
priority_queue<PII, vector<PII>, greater<PII>> heap;
dist[1] = 0;
heap.push({0, 1}); // {距离, 点编号}

while (heap.size()) {
    auto t = heap.top(); heap.pop();
    int ver = t.second, distance = t.first;
    if (st[ver]) continue;
    st[ver] = true;

    for (int i = h[ver]; i != -1; i = ne[i]) {
        int j = e[i];
        if (dist[j] > distance + w[i]) {
            dist[j] = distance + w[i];
            heap.push({dist[j], j});
        }
    }
}

7. Bellman-Ford 算法

作用与特点

  • 作用:求单源最短路,允许图中有负权边。
  • 负权回路:如果图中存在一个环,且环上的边权总和为负数,那么绕这个环转一圈距离就会变小无限次,最短路就不存在。Bellman-Ford 可以检测负权回路。
  • 核心做法:对所有的边进行 $n-1$ 次松弛操作。如果第 $n$ 次循环还能松弛,说明存在负环。

8. SPFA 算法 (Shortest Path Faster Algorithm)

核心思想

  • 本质:它是 Bellman-Ford 算法的队列优化版。
  • 优化逻辑:Bellman-Ford 每次盲目检查所有的边太慢了。SPFA 发现:只有当一个点的最短距离变小了,它才有资格去更新它的邻居。所以用一个队列把“距离变小了的点”存起来。在大多数没有负环的图里,SPFA 运行速度极快。

9. Floyd 算法

作用与精妙代码

  • 作用:求多源汇最短路径(图里任意两个点之间的最短距离),允许负权边,但不能有负环。
  • 核心思想(动态规划):三层循环。外层枚举中转站 $k$($1 \to n$),内层枚举起点 $i$ 和终点 $j$。如果通过 $k$ 中转能让路程变短,就更新它: $$d[i][j] = \min(d[i][j], d[i][k] + d[k][j])$$
  • 代码极短: cpp for (int k = 1; k <= n; k++) for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);

10. Prim 算法

作用

求最小生成树 (MST)。给定一张带权连通图,用最少的边(刚好 $n-1$ 条)把所有点连起来,且所有边的总权值最小。

做法

Prim 算法和 Dijkstra 极其相似: * Dijkstra 是求点到起点的距离,Prim 是求点到当前生成树的距离。 * 每次挑一个离生成树最近的点加入树中,并更新其他点到生成树的距离。


11. Kruskal 算法

作用与直观步骤

另一种求最小生成树的方法,代码简单且极其直观。 1. 排序:把图中的所有边按照权值从小到大排序。 2. 挑选:从小到大扫描每条边 $(u, v)$: * 用并查集判断 $u$ 和 $v$ 是否已经连通。 * 如果不连通,说明没有成环,把这条边选入生成树,并将 $u$ 和 $v$ 合并。 * 直到选够 $n-1$ 条边为止。


12. 染色法判定二分图

什么是二分图?

如果能把图中的所有点分成两个集合,使得图里所有的边都横跨在两个集合之间(同一个集合内部的点之间没有连线),这张图就是二分图。

做法(DFS / BFS)

遍历整张图,用两种颜色(比如 1 和 2)轮流涂色: * 如果搜索到一个未涂色的邻居,把它涂成与当前点不同的颜色。 * 如果发现一个邻居已经被涂过色,且颜色和当前点相同,说明发生了冲突,这就不是二分图。


13. 匈牙利算法

作用

解决二分图的最大匹配问题(例如:媒婆给单身男女牵线,如何让成功牵手的情侣对数最多?)。

核心思想

“先到先得,择优谦让”。如果男生 A 想选女生 B,但 B 已经被男生 C 选走了,算法会尝试去问 C:“你能不能换个备胎?把 B 让给 A。”如果 C 能找到新备胎,A 就成功配对,否则 A 只能再找别人。


—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

关于火龙

  • 关于我们
  • 学员获奖
  • 预约试听
  • ACM课程
  • CSP课程
  • 学习指南

帮助中心

  • 用户协议
  • 打字练习 HOT
  • 在线画图
  • DevC++下载
  • CSP报名
  • GESP官网

推荐课程

  • C++零基础入门(可试看)
  • C++进阶提升
  • GESP考级辅导
  • GESP打卡
  • CSP-J/S打卡

公众号

火龙信奥公众号二维码

© 2017-2026 义乌市睿码科技有限公司版权所有 浙ICP备2021013995号

火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码