拓扑排序

来源:互联网 发布:电信网络诈骗类型 编辑:程序博客网 时间:2024/05/21 21:46
        关于拓扑排序我有个印象就是最开始是个高中同学问我能不能帮她写个拓扑排序。那时我还不会,当然,直到放假后假期我才学会了。首先介绍一下什么是拓扑排序吧,通常我们描述的排序是按数据的大小排序,并且各种算法,各种奇巧淫技。拓扑排序最大的区别就是它并不是按照数据的大小排序的,我把理解为是对一种先后关系之间的排序,举个例子。

        假如你对你的女神表白了,很不幸,女神是学霸,说你只有精通CS专业课,她才能接受你的表白,为了追到女神,你决定下定决心成为大神,然后夺得女神芳心。然而成为大神之路是很艰辛的,要学数学,要学数据结构,要学算法,要学C语言,要学操作系统,要学编译原理,要学计算机原理。于是你找到了各科目的的老师。数据结构老师说:学数据结构要有C语言的基础,并且要有数学的基础;算法老师告诉你,算法的知识需要坚实的数据结构和数据基础;编译原理老师告诉你要先学数据结构和算法然后才能选编译原理;计算机原理老师告诉你,需要数学知识才能学号计算机原理。向各个老师一问,你已经晕了,脑中只有一个概念就是,学这个要先学那个。经过最终整理后你得到了如下的关系图。但是这么多内容,从应该用什么顺序来学习才能保证在学习某一科目之前已经学习了其先修知识呢,这真是个头疼的的问题。幸运的是,你得知使用拓扑排序就能搞定。    

                                                


                                            


                                          


                                                      


                                                              


                                                                      


                                                                                             

                                                                                   


                                                                                                       


        从最终得到的路径顺序和演示图的变化能得到一个非常重要的信息,也就是拓扑排序是怎么排序的。就是在每个步骤执行时,必须保证没有任何前期需要依赖的东西,比如我要上网,那么就要求有电脑和网络连接,使用电脑又要求有电,于是对这几者的拓扑排序即为:网络连接-->有电-->电脑-->上网。上图中即表现为没有指向它的箭头,即入度,(指向某个顶点的箭头叫做入度,某个顶点指向其他顶点的箭头叫做出度)。说明执行这一步所需要的所有前期准备工作已经准备好了,可以顺利向下执行了。
得到这个满足所有依赖情况的路径就是拓扑排序的任务。对拓扑排序就个基本了解后,下面说说拓扑排序的代码实现。
        在拓扑排序中我们只用到了入度,所以,我们定义一个数组来保存每个顶点的入度,然后用一个二维数组表示输入的数据,即顶点+该顶点的邻接顶点。输入数据完成后计算出所以顶点的入度,数据构造好之后,就可以开始拓扑排序了,首先找到入度为0的节点,然后将其从图中删除,并且同时删除该顶点所有执行其他顶点的箭头(出度),即更新剩下顶点的入度,然后再找出入度为0的顶点,从图中删除它,更新剩余顶点的入度。不断重复这个过程,知道图中的所有顶点都清空后,拓扑排序过程就完成了。值得注意的是拓扑排序的结过不是唯一的,因为在某一时刻,可能同时存在多个入度为0的顶点,我们任意选取即可,比如上图排序结过中C语言和数学的位置是可以互换的。
        代码中为了方便的操作,代码中分别用数字表示以上顶点:
        下面C++代码实现拓扑排序:

#include <iostream>#include <map>#include <string>#include <queue>#include <vector>using namespace std;int a[100];//保存顶点的入度,下标表示顶点编号,值表示入度int main(int argc, char const *argv[]){    int n, m, k, name,t;    map<int, vector<int> > points;    cin >> n;//输入顶点个数    for (int i = 0; i < n; i++)    {        cin >> name >> m;//输入顶点的名字和邻接顶点数        vector<int> adjs;//使用vector保存该顶点的邻接顶点        for (int j = 0; j < m; j++)        {            cin >> t;            adjs.push_back(t);        }        points[name] = adjs;//保存邻接顶点    }    for (map<int, vector<int> >::iterator iter = points.begin(); iter != points.end(); ++iter)        for (int i = 0; i < points[iter->first].size(); i++)            a[points[iter->first][i]]++;//计算每个顶点的入度        queue<int> order;//使用队列模拟排序操作    for (int i = 1; i <= n; i++)        if (a[i] == 0)//首先入度为0的顶点插入队列            order.push(i);    int mk = 0;    while (mk < n)//如果还没有输出所有顶点    {        int temp = order.front();        cout << temp << " ";        order.pop();        for (int i = 0; i < points[temp].size(); i++)        {            a[points[temp][i]]--;//更新当前顶点的邻接顶点的入度            if (a[points[temp][i]] == 0)//如果其入度为空,存入队列                order.push(points[temp][i]);        }        mk++;    }    return 0;}/*上图中的测试数据91 3 3 4 52 2 4 53 2 6 74 5 3 6 7 8 55 2 7 86 1 77 1 98 1 79 0*/


        排序结果


        路径结果依次对应的是:数学--C语言--数据结构--计算机原理--算法--操作系统--编译原理--大神--女神,很明显这个过程是符合要求的。所以拓扑排序就完成任务了



0 0