正在载入交互式动画窗口请稍等

拓扑排序-栈 可视化交互式动画版

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

有向无环图 (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 文件中执行的编译任务的顺序
    • 数据序列化
    • 解决链接器中的符号依赖性

相关文章:  

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

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

无论你的目标是考试成功、职业发展,还是纯粹的兴趣,这个数据结构和算法可视化的网站都会是一个无价的资源。

前往这个网站,开始你的学习之旅吧!

这些是常见的:【C语言描述】《数据结构和算法》数据结构JAVA实现 数据结构与算法基础(青岛大学-王卓)数据结构与算法王道数据结构c语言实现 速成数据结构期末考前救急 数据结构视频C语言版教程 数据结构严蔚敏 数据结构郝斌 数据结构考研 JAVA数据结构算法与基础 数据结构王道 2022数据结构学习 数据结构小甲鱼 王卓 学习数据结构 数据结构浙江大学 数据结构复习 数据结构马士兵 数据结构零基础教程 数据结构和算法 数据结构入门 考研数据结构习题讲解 数据结构期末复习 计算机二级