Topological Sort Animation Demo Visualize your code with animations

图码-数据结构可视化动画版

有向无环图 (DAG) 的拓扑排序 是顶点的线性排序,使得对于每个有向边 uv,顶点 u 在排序中 位于 v之前。

注意: 如果图不是 DAG ,则无法对图进行拓扑排序。

例子:

输入: 图表:

例子

例子

输出: 5 4 2 3 1 0
解释: 拓扑排序中的第一个顶点始终是入度为 0 的顶点(没有入边的顶点)。 下图的拓扑排序为“5 4 2 3 1 0”。 一张图可以有不止一种拓扑排序。 下图的另一种拓扑排序是“4 5 2 3 1 0”。

拓扑排序 与深度优先遍历(DFS) : 

DFS 中,我们打印一个顶点,然后递归地调用 DFS 来获取它的相邻顶点。 在拓扑排序中,我们需要在其相邻顶点之前打印一个顶点。 

例如, 在上面给出的图中,顶点“5”应该在顶点“0”之前打印,但与 DFS 不同的是,顶点“4”也应该在顶点“0”之前打印。 所以拓扑排序与DFS不同。 例如,所示图的DFS是“5 2 3 1 0 4”,但这不是拓扑排序。

拓扑排序算法:

先决条件 DFS

我们可以修改 DFS 来找到图的拓扑排序。 深度优先搜索 中, 

  • 我们从一个顶点开始,首先打印它,然后 
  • 对其相邻顶点递归调用DFS。 

在拓扑排序中:

  • 我们使用临时堆栈。 
  • 我们不会立即打印顶点, 
  • 我们首先对其所有相邻顶点递归调用拓扑排序,然后将其压入堆栈。 
  • 最后,打印堆栈的内容。 

注意: 只有当顶点的所有相邻顶点(及其相邻顶点等)都已在堆栈中时,才会将顶点压入堆栈

方法:

  • 创建一个 堆栈 来存储节点。
  • 初始化大小为 N的 访问 数组 来保存访问节点的记录。
  • 运行从0 到N 的循环
  • 如果访问 数组中的 节点未标记为 True ,则调用递归函数进行拓扑排序并执行以下步骤:
    • 访问 数组中将当前节点标记为 True
    • 在与当前节点有向边的所有节点上运行循环
    • 如果该节点在 访问 数组中未标记为 True
      • 在节点上递归调用拓扑排序函数
    • 将当前节点压入堆栈。
  • 打印堆栈中的所有元素。

图示拓扑排序算法:

下图是上述方法的说明:

拓扑排序

拓扑排序总体流程

步骤1:

  • 我们从节点 0 开始 DFS,因为它有零个传入节点
  • 我们将节点 0 压入堆栈并移动到具有最小数量相邻节点的下一个节点,即节点 1。

文件

第2步:

  • 在这一步中,由于该节点没有相邻节点,因此将节点1压入堆栈并移动到下一个节点。

文件

步骤3:

  • 在这一步中,我们选择节点2,因为它在0和1之后的相邻节点数最少。
  • 我们将节点 2 称为 DFS,并将从节点 2 开始遍历的所有节点按相反顺序压入。
  • 所以先推 3 然后推 2 。

文件

步骤4:

  • 我们现在将节点 4 称为 DFS
  • 因为 0 和 1 已经存在于堆栈中,所以我们只需将节点 4 压入堆栈并返回。

文件

第5步:

  • 在这一步中,因为 5 的所有相邻节点都已经在堆栈中,所以我们将节点 5 压入堆栈并返回。

文件

步骤6: 这是拓扑排序的最后一步,我们从堆栈中弹出所有元素并按顺序打印它。

下面是上述方法的实现:

C++

// A C++ program to print topological
// sorting of a DAG
#include <bits/stdc++.h>
using namespace std;
 
// Class to represent a graph
class Graph {
    // No. of vertices'
    int V;
 
    // Pointer to an array containing adjacency listsList
    list<int>* adj;
 
    // A function used by topologicalSort
    void topologicalSortUtil(int v, bool visited[],
                             stack<int>& Stack);
 
public:
    // Constructor
    Graph(int V);
 
    // function to add an edge to graph
    void addEdge(int v, int w);
 
    // prints a Topological Sort of
    // the complete graph
    void topologicalSort();
};
 
Graph::Graph(int V)
{
    this->V = V;
    adj = new list<int>[V];
}
 
void Graph::addEdge(int v, int w)
{
    // Add w to v’s list.
    adj[v].push_back(w);
}
 
// A recursive function used by topologicalSort
void Graph::topologicalSortUtil(int v, bool visited[],
                                stack<int>& Stack)
{
    // Mark the current node as visited.
    visited[v] = true;
 
    // Recur for all the vertices
    // adjacent to this vertex
    list<int>::iterator i;
    for (i = adj[v].begin(); i != adj[v].end(); ++i)
        if (!visited[*i])
            topologicalSortUtil(*i, visited, Stack);
 
    // Push current vertex to stack
    // which stores result
    Stack.push(v);
}
 
// The function to do Topological Sort.
// It uses recursive topologicalSortUtil()
void Graph::topologicalSort()
{
    stack<int> Stack;
 
    // Mark all the vertices as not visited
    bool* visited = new bool[V];
    for (int i = 0; i < V; i++)
        visited[i] = false;
 
    // Call the recursive helper function
    // to store Topological
    // Sort starting from all
    // vertices one by one
    for (int i = 0; i < V; i++)
        if (visited[i] == false)
            topologicalSortUtil(i, visited, Stack);
 
    // Print contents of stack
    while (Stack.empty() == false) {
        cout << Stack.top() << " ";
        Stack.pop();
    }
 
    delete[] visited;
}
 
// Driver Code
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 the given "
            "graph \n";
 
    // Function Call
    g.topologicalSort();
 
    return 0;
}


            

Java

Python3

Javascript

输出

以下是给定图的拓扑排序
5 4 2 3 1 0

时间复杂度: O(V+E)。 上面的算法只是带有额外堆栈的 DFS。 所以时间复杂度与DFS辅助空间相同
O(V)。 堆栈需要额外的空间

注意: 这里,我们也可以使用向量来代替堆栈。 如果使用向量,则以相反的顺序打印元素以获得拓扑排序。

拓扑排序的应用:

  • 拓扑排序主要用于根据作业之间给定的依赖关系来调度作业。 
  • 在计算机科学中,此类应用出现在:
    • 指令调度
    • 在电子表格中重新计算公式值时公式单元格计算的顺序
    • 逻辑综合
    • 确定要在 make 文件中执行的编译任务的顺序
    • 数据序列化
    • 解决链接器中的符号依赖性

相关文章:  

  • 卡恩的拓扑排序算法
  • 有向无环图的所有拓扑排序

如果您发现任何不正确的内容,或者如果您想分享有关上述主题的更多信息,请发表评论

Whether your goal is exam success, career development, or pure interest, this data structure and algorithm visualization website will be an invaluable resource.

Go to this website and start your learning journey!

These are common ones: [C Language Description] Data Structures and Algorithms Data Structure JAVA Implementation Fundamentals of Data Structures and Algorithms (Qingdao University - Wang Zhuo) Data Structures and Algorithms Wangdao Data Structure C Language Implementation Crash Course Data Structures Final Exam Rescue Data Structure Video C Language Edition Tutorial Data Structure Yan Weimin Data Structure Hao Bin Data Structure Postgraduate Entrance Exam JAVA Data Structure Algorithms and Fundamentals Data Structure Wangdao 2022 Data Structure Learning Data Structure Little Turtle Wang Zhuo Learning Data Structure Data Structure Zhejiang University Data Structure Review Data Structure Ma Soldier Data Structure Zero-Based Tutorial Data Structures and Algorithms Data Structure Introduction Postgraduate Data Structure Exercise Explanation Data Structure Final Review Computer Level 2