Topological Sorting เรียงลำดับ Vertex ของ Directed Graph ให้ Edge (u,v) ทุกอันมี u อยู่ก่อน v ได้ก็ต่อเมื่อ Graph เป็น DAG (Directed Acyclic Graph) → ไม่มี Cycle
Algorithm: Kahn's Algorithm (BFS-based)
- คำนวณ In-degree ของทุก Vertex
- Enqueue Vertex ที่ In-degree = 0 ทั้งหมด
- Dequeue Vertex u → Add to Result → ลด In-degree ของ Neighbors
- ถ้า Neighbor In-degree กลายเป็น 0 → Enqueue
- ถ้า Result Size = V → No Cycle (Topological Order Valid)
- ถ้า Result Size < V → มี Cycle (Topological Sort Impossible)
Algorithm: DFS-based (Recursive)
- DFS ทุก Vertex, Track Color (WHITE/GRAY/BLACK)
- ถ้าเจอ GRAY Neighbor → Back Edge → Cycle Detected
- Post-order: Push Vertex to Stack หลัง Visit ครบ
- Reverse Stack = Topological Order
โค้ด C++ (Both Methods)
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
class Graph {
int V;
vector> adj;
public:
Graph(int v) : V(v), adj(v) {}
void addEdge(int u, int v) { adj[u].push_back(v); }
// Kahn's Algorithm (BFS) - Returns {hasCycle, topoOrder}
pair> kahnTopologicalSort() {
vector indegree(V, 0);
for (int u = 0; u < V; u++)
for (int v : adj[u]) indegree[v]++;
queue q;
for (int i = 0; i < V; i++)
if (indegree[i] == 0) q.push(i);
vector topo;
int visited = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
topo.push_back(u);
visited++;
for (int v : adj[u]) {
if (--indegree[v] == 0) q.push(v);
}
}
return {visited != V, topo}; // true = has cycle
}
// DFS-based Topological Sort
pair> dfsTopologicalSort() {
vector color(V, 0); // 0=WHITE, 1=GRAY, 2=BLACK
vector topo;
bool hasCycle = false;
function dfs = [&](int u) {
color[u] = 1; // GRAY
for (int v : adj[u]) {
if (color[v] == 0) dfs(v);
else if (color[v] == 1) hasCycle = true; // Back Edge
}
color[u] = 2; // BLACK
topo.push_back(u);
};
for (int i = 0; i < V; i++)
if (color[i] == 0) dfs(i);
reverse(topo.begin(), topo.end());
return {hasCycle, topo};
}
void printGraph() {
for (int u = 0; u < V; u++) {
cout << u << " -> ";
for (int v : adj[u]) cout << v << " ";
cout << "\n";
}
}
};
int main() {
// Example from article: 6 vertices
Graph g(6);
g.addEdge(5, 2);
g.addEdge(5, 0);
g.addEdge(4, 0);
g.addEdge(4, 1);
g.addEdge(2, 3);
g.addEdge(3, 1);
cout << "Graph:\n"; g.printGraph();
// Kahn's
auto [hasCycle1, topo1] = g.kahnTopologicalSort();
cout << "\nKahn's Algorithm:\n";
cout << "Has Cycle: " << (hasCycle1 ? "YES" : "NO") << "\n";
if (!hasCycle1) {
cout << "Topological Order: ";
for (int v : topo1) cout << v << " ";
cout << "\n";
}
// DFS
auto [hasCycle2, topo2] = g.dfsTopologicalSort();
cout << "\nDFS Algorithm:\n";
cout << "Has Cycle: " << (hasCycle2 ? "YES" : "NO") << "\n";
if (!hasCycle2) {
cout << "Topological Order: ";
for (int v : topo2) cout << v << " ";
cout << "\n";
}
// Test with Cycle
Graph g2(3);
g2.addEdge(0, 1);
g2.addEdge(1, 2);
g2.addEdge(2, 0); // Cycle!
auto [cycle, _] = g2.kahnTopologicalSort();
cout << "\nGraph with Cycle (0->1->2->0):\n";
cout << "Has Cycle: " << (cycle ? "YES" : "NO") << "\n";
return 0;
}
ผลลัพธ์
Graph: 0 -> 1 -> 2 -> 3 3 -> 1 4 -> 0 1 5 -> 2 0 Kahn's Algorithm: Has Cycle: NO Topological Order: 5 4 2 3 1 0 DFS Algorithm: Has Cycle: NO Topological Order: 5 4 2 3 1 0 Graph with Cycle (0->1->2->0): Has Cycle: YES
Comparison
| Aspect | Kahn's (BFS) | DFS |
|---|---|---|
| Cycle Detection | Result Size < V | Back Edge (Gray→Gray) |
| Space | O(V) Queue + Indegree | O(V) Color + Stack |
| Multiple Valid Orders | Depends on Queue Order | Depends on Adj List Order |
| Lexicographically Smallest | Use Priority Queue | Sort Adj Lists |
| Parallelizable | Yes (Level by Level) | No (Inherently Sequential) |