图论—拓扑排序
一、简述
拓扑排序是针对 有向无环图(DAG, Directed Acyclic Graph) 的一种排序算法,其核心目标是:将图中所有顶点排成一个线性序列,使得对于图中任意一条有向边 u—>v,顶点 u 都排在顶点 v 的前面。
二、核心概念
三、实现算法
1. 卡恩算法(Kahn's Algorithm)—— 基于入度的贪心算法
核心思想
(1)初始化一个队列,将所有入度为 0 的顶点加入队列。
(2)循环从队列中取出顶点 u,加入拓扑序列,并遍历 u 的所有邻接顶点 v:
(3)重复上述步骤,直到队列为空。
(4)若最终拓扑序列的长度等于图中顶点总数,则排序成功;否则图中存在环。
复杂度分析
参考代码
#include <iostream> #include <vector> #include <queue> using namespace std; // 拓扑排序:Kahn算法 // 参数:n-顶点数,adj-邻接表,返回拓扑序列(空表示存在环) vector<int> kahn(int n, vector<vector<int>>& adj) { vector<int> in_degree(n, 0); // 记录每个顶点的入度 vector<int> result; // 存储拓扑序列 queue<int> q; // 存放入度为0的顶点 // 1. 统计每个顶点的入度 for (int u = 0; u < n; u++) { for (int v : adj[u]) { in_degree[v]++; } } // 2. 初始化队列:入度为0的顶点入队 for (int i = 0; i < n; i++) { if (in_degree[i] == 0) { q.push(i); } } // 3. 处理队列中的顶点 while (!q.empty()) { int u = q.front(); q.pop(); result.push_back(u); // 加入拓扑序列 // 遍历u的邻接顶点,减少入度 for (int v : adj[u]) { in_degree[v]--; if (in_degree[v] == 0) { // 入度为0时入队 q.push(v); } } } // 4. 检查是否存在环:若结果长度≠顶点数,说明有环 if (result.size() != n) { return {}; // 空数组表示存在环 } return result; } // 测试样例 int main() { int n = 6; // 顶点数(0~5) vector<vector<int>> adj(n); // 构建有向边:0→1, 0→2, 1→3, 2→3, 3→4, 3→5 adj[0].push_back(1); adj[0].push_back(2); adj[1].push_back(3); adj[2].push_back(3); adj[3].push_back(4); adj[3].push_back(5); vector<int> topo = kahn(n, adj); cout<<topo.size()<<endl; if (topo.empty()) { cout << "图中存在环,无法拓扑排序!" << endl; } else { cout << "拓扑序列:"; for (int v : topo) { cout << v << " "; } // 可能的输出:0 1 2 3 4 5 或 0 2 1 3 5 4 等 } return 0; }2. 基于 DFS 的拓扑排序
核心思想
注意事项
参考代码
#include <iostream> #include <vector> #include <stack> using namespace std; // 标记顶点是否被访问,避免重复遍历 vector<bool> visited; stack<int> stk; // 存储顶点,用于生成拓扑序列 // DFS 遍历函数 void dfs(int u, vector<vector<int>>& adj) { visited[u] = true; // 标记当前顶点已访问 // 遍历u的所有邻接顶点 for (int v : adj[u]) { if (!visited[v]) { // 未访问过的顶点递归DFS dfs(v, adj); } } // 所有邻接顶点处理完后,当前顶点入栈 stk.push(u); } // 拓扑排序:基于DFS vector<int>DFS(int n, vector<vector<int>>& adj) { visited.assign(n, false); // 初始化访问标记 // 遍历所有顶点,处理非连通图的情况 for (int i = 0; i < n; i++) { if (!visited[i]) { dfs(i, adj); } } // 栈中元素出栈,得到拓扑序列 vector<int> result; while (!stk.empty()) { result.push_back(stk.top()); stk.pop(); } return result; } int main() { int n = 6; vector<vector<int>> adj(n); adj[0].push_back(1); adj[0].push_back(2); adj[1].push_back(3); adj[2].push_back(3); adj[3].push_back(4); adj[3].push_back(5); vector<int> topo = DFS(n, adj); cout << "拓扑序列(DFS版):"; for (int v : topo) { cout << v << " "; } return 0; }