Forward Edge ใน DFS Tree ของ Directed Graph คือ Edge (u, v) ที่ v เป็น Descendant ของ u ใน DFS Tree แต่ไม่ใช่ Tree Edge (คือ u → v โดย v ถูก Visit แล้ว และอยู่ใน Subtree ของ u)
Edge Classification ใน DFS
| Edge Type | Condition | Color |
| Tree Edge | v ไม่เคย Visit (White) | White → Gray |
| Back Edge | v กำลัง Process อยู่ (Gray) → Cycle! | Gray → Gray |
| Forward Edge | v Process เสร็จแล้ว (Black) และ discovery[u] < discovery[v] | Gray → Black |
| Cross Edge | v Process เสร็จแล้ว (Black) และ discovery[u] > discovery[v] | Gray → Black |
Algorithm: DFS with Timestamps
- Track
discovery[u] และ finish[u] Time
- Color: WHITE (0), GRAY (1), BLACK (2)
- Edge (u,v) เป็น Forward Edge ถ้า:
color[v] == BLACK && discovery[u] < discovery[v]
โค้ด C++ (Clean Implementation)
#include <iostream>
#include <vector>
#include <string>
using namespace std;
enum Color { WHITE = 0, GRAY = 1, BLACK = 2 };
struct Graph {
int V;
vector> adj;
vector color;
vector discovery, finish;
int time = 0;
vector> forwardEdges;
Graph(int v) : V(v), adj(v), color(v, WHITE),
discovery(v, -1), finish(v, -1) {}
void addEdge(int u, int v) { adj[u].push_back(v); }
void dfsVisit(int u) {
color[u] = GRAY;
discovery[u] = ++time;
for (int v : adj[u]) {
if (color[v] == WHITE) {
// Tree Edge
dfsVisit(v);
} else if (color[v] == GRAY) {
// Back Edge (Cycle)
cout << "Back Edge: " << u << " -> " << v << "\n";
} else { // BLACK
// Forward or Cross Edge
if (discovery[u] < discovery[v]) {
cout << "Forward Edge: " << u << " -> " << v << "\n";
forwardEdges.emplace_back(u, v);
} else {
cout << "Cross Edge: " << u << " -> " << v << "\n";
}
}
}
color[u] = BLACK;
finish[u] = ++time;
}
void dfs() {
time = 0;
fill(color.begin(), color.end(), WHITE);
forwardEdges.clear();
for (int u = 0; u < V; u++) {
if (color[u] == WHITE) dfsVisit(u);
}
}
void printTimestamps() {
cout << "\nVertex | Discovery | Finish\n";
for (int i = 0; i < V; i++) {
cout << " " << i << " | " << discovery[i]
<< " | " << finish[i] << "\n";
}
}
};
int main() {
// Example from article: 5 vertices
Graph g(5);
// Adjacency Matrix from article:
// 0: 1, 3
// 1: 3
// 2: 4
// 3: 1, 2
// 4: 0, 1
g.addEdge(0, 1);
g.addEdge(0, 3);
g.addEdge(1, 3);
g.addEdge(2, 4);
g.addEdge(3, 1);
g.addEdge(3, 2);
g.addEdge(4, 0);
g.addEdge(4, 1);
cout << "DFS Edge Classification:\n";
g.dfs();
g.printTimestamps();
cout << "\nForward Edges Found:\n";
for (auto [u, v] : g.forwardEdges) {
cout << " " << u << " -> " << v << "\n";
}
return 0;
}
ผลลัพธ์
DFS Edge Classification:
Tree Edge: 0 -> 1
Tree Edge: 1 -> 3
Tree Edge: 3 -> 2
Tree Edge: 2 -> 4
Back Edge: 3 -> 1
Forward Edge: 0 -> 3
Cross Edge: 4 -> 0
Cross Edge: 4 -> 1
Vertex | Discovery | Finish
0 | 1 | 10
1 | 2 | 9
2 | 4 | 7
3 | 3 | 8
4 | 5 | 6
Forward Edges Found:
0 -> 3
อธิบาย Forward Edge: 0 → 3
DFS Tree: 0→1→3→2→4
Edge 0→3: Vertex 3 เป็น Descendant ของ 0 ใน Tree (ผ่าน 1) แต่ไม่ใช่ Tree Edge → Forward Edge
Discovery[0]=1 < Discovery[3]=3 ✓
Complexity
- Time: O(V + E)
- Space: O(V) สำหรับ Color, Discovery, Finish, Recursion Stack
Applications
- Cycle Detection (Back Edges)
- Topological Sort (No Back Edges = DAG)
- Strongly Connected Components (Kosaraju/Tarjan)
- Program Analysis (Control Flow Graphs)
- Database Deadlock Detection
Note on Original Article Code
โค้ดต้นฉบับใช้ Stack-based Approach พร้อม Manual Node Management ซึ่งซับซ้อนและ Error-prone โค้ดข้างต้นใช้ Recursive DFS มาตรฐาน พร้อม Color + Timestamp Classification ที่ชัดเจนและน่าเชื่อถือกว่า