S算法笔记
返回首页
图论算法 7 分钟阅读

图论搜索的拓扑本质:从分层 BFS 到依赖排序

广度优先搜索的自然分层特性与入度缩减的无环依赖判定,是理解复杂调度系统的基石模型。

图的本质:状态与依赖关系网

在算法的世界里,“万物皆可为图”:

  • 网格迷宫的走法:坐标为顶点,上下左右移动为边。
  • 任务编排与编译器依赖:软件包或目标文件为顶点,先修依赖为有向边。
  • 状态机转移:业务状态为顶点,事件触发为有向边。

当图具备“有向”且“无环”的属性时,它被称为 DAG(Directed Acyclic Graph,有向无环图)。DAG 是所有因果链条与任务调度的数学抽象。

广度优先搜索(BFS)的自然分层性

为什么在**所有边权均为非负常数(例如全为 1)**的图上,BFS 求出的路径必然是最短路径?

因为 BFS 天然具备“水波扩散”的分层属性:

  • 第 0 层:起点 (距离 0)
  • 第 1 层:与起点直接相连的所有未访问点(距离 1)
  • 层:从第 层的点一步可达的新顶点(距离

队列的先进先出(FIFO)特性保证了按距离单调不减的顺序逐层扩展。当首次触达终点 时,当前层数即为全局最短距离。

拓扑排序(Kahn 算法)

拓扑排序的目标是将 DAG 的所有顶点排成一个线性序列,使得对于图中的任意有向边 在序列中均出现在 之前。

基于入度削减的 Kahn 算法 是最清晰且直观的工程解法:

// n 个顶点,编号 0 ~ n-1
std::vector<int> in_degree(n, 0);
for (const auto& [u, v] : edges) {
    in_degree[v]++;
}

std::queue<int> q;
for (int i = 0; i < n; ++i) {
    if (in_degree[i] == 0) {
        q.push(i); // 将所有无前置依赖的任务入队
    }
}

std::vector<int> topo_order;
while (!q.empty()) {
    int u = q.front();
    q.pop();
    topo_order.push_back(u);

    for (int v : adj[u]) {
        if (--in_degree[v] == 0) {
            q.push(v); // 前置依赖全部消除,任务就绪
        }
    }
}

// 环检测判断:若拓扑序列长度 < 总顶点数,则图中有环(循环依赖)
bool has_cycle = (topo_order.size() < n);

环路诊断与 DFS 三色标记法

除了 Kahn 算法,DFS 三色标记法也是检测有向图环路的利器:

  • 白色(0):尚未访问。
  • 灰色(1):正在当前递归调用栈中(祖先节点)。若访问到灰色节点,立刻确认发现后向边(Back-edge),存在环路!
  • 黑色(2):该节点及其全部子孙节点均已访问完毕。

拓扑排序不仅是算法题目的常客,更是现代构建工具(Webpack, Bazel)、工作流调度引擎(Airflow)和微服务依赖启动的核心算法。

记录思路,分享理解,让每次练习都产生长期价值。