Kahn 的拓扑排序算法
有向循环图( DAG ) 的拓扑排序是顶点的线性排序,使得对于每个有向边 uv,顶点 u 在排序中位于 v 之前。如果图不是 DAG,则无法对图进行拓扑排序。
例如,下图的拓扑排序是“5 4 2 3 1 0?”。一个图可以有多个拓扑排序。例如,下图的另一种拓扑排序是“4 5 2 0 3 1”。拓扑排序中的第一个顶点始终是入度为 0 的顶点(没有入边的顶点)。
让我们看几个正确解释的例子,
例子:
Input:
Output: 5 4 2 3 1 0
Explanation: The topological sorting of a DAG is done in a order such that for every directed edge uv, vertex u comes before v in the ordering. 5 has no incoming edge. 4 has no incoming edge, 2 and 0 have incoming edge from 4 and 5 and 1 is placed at last.
Input:
Output: 0 3 4 1 2
Explanation: 0 and 3 have no incoming edge, 4 and 1 has incoming edge from 0 and 3. 2 is placed at last.
已经讨论了用于查找拓扑排序的基于 DFS 的解决方案。
解决方案:在本文中,我们将看到另一种在有向无环图 (DAG) 中找到顶点线性排序的方法。该方法基于以下事实:
DAG G 至少有一个入度为 0 的顶点和一个出度为 0 的顶点。
证明:对上述事实有一个简单的证明,即 DAG 不包含循环,这意味着所有路径的长度都是有限的。现在让 S 是从 u(source) 到 v(destination) 的最长路径。因为 S 是最长的路径,所以不能有到 u 的入边,也没有来自 v 的出边,如果发生这种情况,那么 S 就不是最长的路径
=> 入度(u) = 0 和出度(v) = 0
算法:找到 DAG 的拓扑排序所涉及的步骤:
步骤 1:计算 DAG 中存在的每个顶点的入度(传入边数),并将访问节点的计数初始化为 0。
Step-2:选取所有入度为0的顶点,加入队列(入队操作)
Step-3:从队列中移除一个顶点(出队操作)然后。
- 将访问节点的计数增加 1。
- 将其所有相邻节点的度数减少 1。
- 如果相邻节点的入度减少到零,则将其添加到队列中。
第 4 步:重复第 3 步,直到队列为空。
步骤 5:如果访问节点的数量不等于图中的节点数,则对给定的图进行拓扑排序是不可能的。
如何找到每个节点的入度?
有两种方法可以计算每个顶点的入度:
- 取一个度数数组,它将跟踪
遍历边数组,只需将目标节点的计数器加 1。
for each node in Nodes
indegree[node] = 0;
for each edge(src, dest) in Edges
indegree[dest]++
- 时间复杂度:O(V+E)
- 遍历每个节点的列表,然后将连接到它的所有节点的入度增加 1。
for each node in Nodes
If (list[node].size()!=0) then
for each dest in list
indegree[dest]++;
- 时间复杂度:外部for循环将执行V次,内部for循环将执行E次,因此总体时间复杂度为O(V+E)。
算法的整体时间复杂度为 O(V+E)
下面是上述算法的 C++ 实现。该实现使用上面讨论的方法 2 来查找度数。
C++
// A C++ program to print topological
// sorting of a graph using indegrees.
#include
using namespace std;
// Class to represent a graph
class Graph {
// No. of vertices'
int V;
// Pointer to an array containing
// adjacency listsList
list* adj;
public:
// Constructor
Graph(int V);
// Function to add an edge to graph
void addEdge(int u, int v);
// prints a Topological Sort of
// the complete graph
void topologicalSort();
};
Graph::Graph(int V)
{
this->V = V;
adj = new list[V];
}
void Graph::addEdge(int u, int v)
{
adj[u].push_back(v);
}
// The function to do
// Topological Sort.
void Graph::topologicalSort()
{
// Create a vector to store
// indegrees of all
// vertices. Initialize all
// indegrees as 0.
vector in_degree(V, 0);
// Traverse adjacency lists
// to fill indegrees of
// vertices. This step
// takes O(V+E) time
for (int u = 0; u < V; u++) {
list::iterator itr;
for (itr = adj[u].begin();
itr != adj[u].end(); itr++)
in_degree[*itr]++;
}
// Create an queue and enqueue
// all vertices with indegree 0
queue q;
for (int i = 0; i < V; i++)
if (in_degree[i] == 0)
q.push(i);
// Initialize count of visited vertices
int cnt = 0;
// Create a vector to store
// result (A topological
// ordering of the vertices)
vector top_order;
// One by one dequeue vertices
// from queue and enqueue
// adjacents if indegree of
// adjacent becomes 0
while (!q.empty()) {
// Extract front of queue
// (or perform dequeue)
// and add it to topological order
int u = q.front();
q.pop();
top_order.push_back(u);
// Iterate through all its
// neighbouring nodes
// of dequeued node u and
// decrease their in-degree
// by 1
list::iterator itr;
for (itr = adj[u].begin();
itr != adj[u].end(); itr++)
// If in-degree becomes zero,
// add it to queue
if (--in_degree[*itr] == 0)
q.push(*itr);
cnt++;
}
// Check if there was a cycle
if (cnt != V) {
cout << "There exists a cycle in the graph\n";
return;
}
// Print topological order
for (int i = 0; i < top_order.size(); i++)
cout << top_order[i] << " ";
cout << endl;
}
// Driver program to test above functions
int main()
{
// Create a graph given in the
// above diagram
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 << "Following is a Topological Sort of\n";
g.topologicalSort();
return 0;
}
Java
// A Java program to print topological
// sorting of a graph using indegrees
import java.util.*;
// Class to represent a graph
class Graph {
// No. of vertices
int V;
// An Array of List which contains
// references to the Adjacency List of
// each vertex
List adj[];
// Constructor
public Graph(int V)
{
this.V = V;
adj = new ArrayList[V];
for (int i = 0; i < V; i++)
adj[i] = new ArrayList();
}
// Function to add an edge to graph
public void addEdge(int u, int v)
{
adj[u].add(v);
}
// prints a Topological Sort of the
// complete graph
public void topologicalSort()
{
// Create a array to store
// indegrees of all
// vertices. Initialize all
// indegrees as 0.
int indegree[] = new int[V];
// Traverse adjacency lists
// to fill indegrees of
// vertices. This step takes
// O(V+E) time
for (int i = 0; i < V; i++) {
ArrayList temp
= (ArrayList)adj[i];
for (int node : temp) {
indegree[node]++;
}
}
// Create a queue and enqueue
// all vertices with indegree 0
Queue q
= new LinkedList();
for (int i = 0; i < V; i++) {
if (indegree[i] == 0)
q.add(i);
}
// Initialize count of visited vertices
int cnt = 0;
// Create a vector to store result
// (A topological ordering of the vertices)
Vector topOrder = new Vector();
while (!q.isEmpty()) {
// Extract front of queue
// (or perform dequeue)
// and add it to topological order
int u = q.poll();
topOrder.add(u);
// Iterate through all its
// neighbouring nodes
// of dequeued node u and
// decrease their in-degree
// by 1
for (int node : adj[u]) {
// If in-degree becomes zero,
// add it to queue
if (--indegree[node] == 0)
q.add(node);
}
cnt++;
}
// Check if there was a cycle
if (cnt != V) {
System.out.println(
"There exists a cycle in the graph");
return;
}
// Print topological order
for (int i : topOrder) {
System.out.print(i + " ");
}
}
}
// Driver program to test above functions
class Main {
public static void main(String args[])
{
// Create a graph given in the above diagram
Graph g = new Graph(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);
System.out.println(
"Following is a Topological Sort");
g.topologicalSort();
}
}
Python3
# A Python program to print topological sorting of a graph
# using indegrees
from collections import defaultdict
# Class to represent a graph
class Graph:
def __init__(self, vertices):
self.graph = defaultdict(list) # dictionary containing adjacency List
self.V = vertices # No. of vertices
# function to add an edge to graph
def addEdge(self, u, v):
self.graph[u].append(v)
# The function to do Topological Sort.
def topologicalSort(self):
# Create a vector to store indegrees of all
# vertices. Initialize all indegrees as 0.
in_degree = [0]*(self.V)
# Traverse adjacency lists to fill indegrees of
# vertices. This step takes O(V + E) time
for i in self.graph:
for j in self.graph[i]:
in_degree[j] += 1
# Create an queue and enqueue all vertices with
# indegree 0
queue = []
for i in range(self.V):
if in_degree[i] == 0:
queue.append(i)
# Initialize count of visited vertices
cnt = 0
# Create a vector to store result (A topological
# ordering of the vertices)
top_order = []
# One by one dequeue vertices from queue and enqueue
# adjacents if indegree of adjacent becomes 0
while queue:
# Extract front of queue (or perform dequeue)
# and add it to topological order
u = queue.pop(0)
top_order.append(u)
# Iterate through all neighbouring nodes
# of dequeued node u and decrease their in-degree
# by 1
for i in self.graph[u]:
in_degree[i] -= 1
# If in-degree becomes zero, add it to queue
if in_degree[i] == 0:
queue.append(i)
cnt += 1
# Check if there was a cycle
if cnt != self.V:
print ("There exists a cycle in the graph")
else :
# Print topological order
print (top_order)
g = Graph(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);
print ("Following is a Topological Sort of the given graph")
g.topologicalSort()
# This code is contributed by Neelam Yadav
Javascript
输出:
Following is a Topological Sort
4 5 2 0 3 1
复杂性分析:
- 时间复杂度: O(V+E)。
外部 for 循环将执行 V 次,内部 for 循环将执行 E 次。 - 辅助空间: O(V)。
队列需要存储图的所有顶点。所以所需的空间是 O(V)