【图论】拓扑排序

本文介绍了拓扑排序的基本概念,包括其关键性质及应用场景,并详细解释了如何通过拓扑排序来解决图论中的常见问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法复习——拓扑排序

板子

inline void topsort()
{
    queue<int>q;
    for(int i=1;i<=n;i++)
        if(!deg[i]) q.push(i);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        top[++pos]=x;
        for(int i=head[x];i;i=e[i].nxt)
        {
            int y=e[i].to;deg[y]--;
            //dp递推
            if(!deg[y]) q.push(y);
        }
    }
}

1.性质

1-1.性质1

在DAG中,对于任何的点 iiijjj 的有向边,定义点 iii 的拓扑序为 tit_iti ,点 jjj 的拓扑序为 tjt_jtj ,满足 ti<tjt_i<t_jti<tj

1-2.性质2

在DAG中,图的拓扑序唯一,当且仅当它的拓扑层数与点数相等。

拓扑层数——BFS层数,可以理解为图中最长链的长度,最简情况下为链

1-3.性质3

在DAG中,如果点 iii 有一段到达点 jjj 的路径,定义点 iii 的拓扑序为 tit_iti ,点 jjj 的拓扑序为 tjt_jtj ,满足 ti<tjt_i<t_jti<tj。(反之不一定成立,性质4为反例)

1-4.性质4

一个DAG中,点 jjj 为满足 ti<tjt_i<t_jti<tj,且 iii 有一条指向 jjj 的边时,tjt_jtj 最小的点,

则对于任意一个拓扑序在 tit_ititjt_jtj 的区间内的点 ppp,点 iii 与点 ppp 之间没有路径,即点 iii 一定不能到达点 ppp

证明:反证法。如果存在一个点kkk,满足 ti<tk<tjt_i<t_k<t_jti<tk<tj 且点 iii 可到达 kkk,则两者的路径上存在一点 qqq,满足点 qqq 为从点 iii 为起点,点 kkk 为终点的路径上的第一个点,则其与iii 间存在一条边。由拓扑序的基本定义可得tj≤tqt_j \leq t_qtjtq。而因为点 qqq 有一条可到达点 kkk 的路径,由拓扑序的拓展性质1可得 tq<tkt_q<t_ktq<tk , 推出 tj<tkt_j<t_ktj<tk ,与假设不符,故原命题正确。

2.应用

2-1.用于递推过程的遍历顺序

  • 在DAG中,用拓扑序求最短路,最长路,可做到O(n)O(n)O(n)复杂度

2-2.判断图中是否有环

在没有DAG保证的图中运行一遍拓扑排序,如果拓扑序中的点数小于图的总点数,即有环,否则为DAG。
其中,拓扑序中的点的个数与总点数的差即为所有环的总点数

正确性证明:在图中有环的情况下,环中每个点在被遍历到时的入度均不为0,无法入队,无法被拓扑序记录。

2-3.利用性质处理图中可达性问题

性质如上

### 拓扑排序的概念 拓扑排序是指在一个有向无环图(Directed Acyclic Graph, DAG)中,将所有顶点排列成一个线性序列的过程[^1]。该序列需满足如下条件:如果存在一条从顶点 \( u \) 到顶点 \( v \) 的路径,则在最终的线性序列中,\( u \) 必须位于 \( v \) 之前。 只有当图为有向无环图时,才可能完成拓扑排序[^2]。这是因为如果有环路存在,则无法找到一种顺序来满足所有的约束条件。 --- ### 拓扑排序的实现方法 #### 方法一:基于深度优先搜索 (DFS) 通过深度优先遍历的方式可以实现拓扑排序。其核心思想是在 DFS 遍历时记录每个节点的退出时间,并按照退出时间逆序构建拓扑序列。 以下是 C++ 中基于 DFS 的拓扑排序实现: ```cpp #include <iostream> #include <vector> using namespace std; void dfs(int node, vector<vector<int>> &adjList, vector<bool> &visited, vector<int> &result) { visited[node] = true; for(auto neighbor : adjList[node]) { if(!visited[neighbor]) { dfs(neighbor, adjList, visited, result); } } result.push_back(node); // 记录退出顺序 } vector<int> topologicalSort_DFS(vector<vector<int>> &graph, int n) { vector<bool> visited(n, false); vector<int> result; for(int i = 0; i < n; ++i){ if(!visited[i]){ dfs(i, graph, visited, result); } } reverse(result.begin(), result.end()); // 反转退出顺序得到拓扑序列 return result; } ``` 此代码片段展示了如何利用深度优先搜索生成拓扑排序结果[^3]。 #### 方法二:基于入度计数 (Kahn's Algorithm) 另一种常见的方法是 Kahn 提出的算法,它依据节点的入度计算来进行排序。具体过程为先找出所有入度为零的节点并加入队列;随后依次移除这些节点及其关联边,更新剩余节点的入度直到全部处理完毕为止。 下面是 Python 版本的 Kahn 算法实现: ```python from collections import deque def topological_sort_kahn(graph): indegree = {node: 0 for node in range(len(graph))} queue = deque() topo_order = [] # Calculate the initial indegrees of all nodes. for node in range(len(graph)): for neighbor in graph[node]: indegree[neighbor] += 1 # Initialize a queue with all nodes that have zero indegree. for node in indegree: if indegree[node] == 0: queue.append(node) while queue: current_node = queue.popleft() topo_order.append(current_node) for neighbor in graph[current_node]: indegree[neighbor] -= 1 if indegree[neighbor] == 0: queue.append(neighbor) if len(topo_order) != len(graph): # Check for cycles raise ValueError("Cycle detected! Topological sort not possible.") return topo_order ``` 上述代码实现了基于入度减少策略的拓扑排序逻辑[^4]。 --- ### 拓扑排序的应用场景 拓扑排序广泛应用于各种实际问题之中,尤其是在涉及依赖关系的情况下尤为有效。例如,在项目管理领域,它可以用来安排任务执行次序以确保前置工作已完成后再启动后续作业;又或者用于编译器优化过程中决定变量声明与初始化间的先后关系等情形下均能发挥重要作用。 另外一个重要应用实例便是课程表规划问题——给定一系列必修课以及它们之间存在的先决条件限制,求解是否存在合理的学习进度安排方案使得学生能够顺利完成学业目标的同时也遵循既定规则的要求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值