zoukankan      html  css  js  c++  java
  • [转]拓扑排序

    转自:http://blog.csdn.net/midgard/article/details/4101025#comments

    拓扑排序是对有向无环图的一种排序。表示了顶点按边的方向出现的先后顺序。如果有环,则无法表示两个顶点的先后顺序。

    在现实生活中,也会有不少应用例子,比如学校课程布置图,要先修完一些基础课,才可以继续修专业课。
    一个简单的求拓扑排序的算法:首先要找到任意入度为0的一个顶点,删除它及所有相邻的边,再找入度为0的顶点,以此类推,直到删除所有顶点。顶点的删除顺序即为拓扑排序。
        很容易得到拓扑排序的伪代码:
        void TopSort(Graph g)
        {
            for (int i=0; i<vertexnum; i++)
            {
                vertex v = FindZeroIndegree(g);
                if (v is not vertex)        
                    cout <<"the graph has cycle"<<endl;
                cout << v ;
                foreach vertex w adjacent to v
                    w.indegree--;
            }
        }
       
        同样以上图为例,对于该图进行拓扑排序会得到:v1 v2 v5 v4 v3 v7 v6 或者v1 v2 v5 v4 v7 v3 v6 。
        仍然利用上一贴图的构建方法,进行验证。
        代码实现:
     
     
    #include <iostream>
    using namespace std;

    #define MAX_VERTEX_NUM    20
    struct adjVertexNode 
    {
        int adjVertexPosition;
        adjVertexNode* next; 
    };
    struct VertexNode
    {
        char data[2];
        adjVertexNode* list;
        int indegree;
    }; 
    struct Graph
    {
        VertexNode VertexNode[MAX_VERTEX_NUM];
        int vertexNum;
        int edgeNum;
    };

    void CreateGraph (Graph& g)
    {
         int i, j, edgeStart, edgeEnd;
         adjVertexNode* adjNode;
         cout << "Please input vertex and edge num (vnum enum):" <<endl;
         cin >> g.vertexNum >> g.edgeNum;
         cout << "Please input vertex information (v1)/n note: every vertex info end with Enter" <<endl;
         for (i=0;i<g.vertexNum;i++) 
         {
             cin >> g.VertexNode[i].data; // vertex data info.
             g.VertexNode[i].list = NULL; 
             g.VertexNode[i].indegree = 0;
         }
         cout << "input edge information(start end):" <<endl;
         for (j=0; j<g.edgeNum; j++) 
         { 
             cin >>edgeStart >>edgeEnd; 
             adjNode = new adjVertexNode; 
             adjNode->adjVertexPosition = edgeEnd-1// because array begin from 0, so it is j-1
             adjNode->next=g.VertexNode[edgeStart-1].list; 
             g.VertexNode[edgeStart-1].list=adjNode; 
             //每增加一条边,则边的End顶点的入度加1
             g.VertexNode[edgeEnd-1].indegree++; 
         }
    }

    void PrintAdjList(const Graph& g)
    {
        cout << "The adjacent list for graph is:" << endl; 
        for (int i=0; i < g.vertexNum; i++)
        {
            cout<< g.VertexNode[i].data << "->";
            adjVertexNode* head = g.VertexNode[i].list;
            if (head == NULL)
                cout << "NULL";
            while (head != NULL)
            {
                cout << head->adjVertexPosition + 1 <<" ";
                head = head->next;
            }
            cout << endl;
        }
    }

    VertexNode& FindZeroIndegree(Graph& g)
    {
        for (int i=0; i<g.vertexNum; i++)
        {
            if (g.VertexNode[i].indegree==0)
                return g.VertexNode[i];
        }
        return g.VertexNode[0];
    }
    void TopSort(Graph& g)
    {
        cout << "The topsort is:" <<endl;
        for (int i=0; i<g.vertexNum; i++)
        {
            VertexNode& v = FindZeroIndegree(g);
            if (v.indegree!=NULL)
                cout << "The graph has cycle, can not do topsort"<<endl;
            // print graph as topsort.
            cout<< v.data << " ";
            // for each vertex w adjacent to v, --indegree
            adjVertexNode* padjv = v.list;
            while (padjv!=NULL)
            {//!!这个算法这里破坏了原图中的入度信息。最后入度均为1
                g.VertexNode[padjv->adjVertexPosition].indegree--;
                padjv = padjv->next;
            }
            //避免入度信息均为零FindZeroIndegree找到删除的顶点,将删除的顶点入度置为1
            v.indegree++;
        }
        cout << endl;
    }

    void DeleteGraph(Graph &g)
    {
        for (int i=0; i<g.vertexNum; i++)
        {
            adjVertexNode* tmp=NULL;
            while(g.VertexNode[i].list!=NULL)
            {
                tmp = g.VertexNode[i].list;
                g.VertexNode[i].list = g.VertexNode[i].list->next;
                delete tmp;
                tmp = NULL;
            }
        }
    }
    int main(int argc, const char** argv)
    {
        Graph g;
        CreateGraph(g);
        PrintAdjList(g);
        TopSort(g);
        DeleteGraph(g);

        return 0;
    }
     运行结果:
     
         从上面的代码能发现FindZeroIndegree的时间复杂度为O(|V|),TopSort的时间复杂度为O(|V|2)
         原因在于,每次删除顶点,只有邻接点需要调整入度,但FindZeroIndegree却是遍历了所有顶点,甚至已经删除的顶点。
         更为合理的方法是将每次遍历得出的入度为0的顶点放入一个队列。
    void TopSort2(Graph& g)
    {
        queue<VertexNode> q;
        for (int i=0i<g.vertexNumi++)
        {
            if (g.VertexNode[i].indegree == 0)
                q.push(g.VertexNode[i]);
        }
        int count = 0;
        cout << "The topsort is:" <<endl;
        while (!q.empty())
        {
            VertexNode v = q.front();
            q.pop();
            cout<< v.data << " "
            count++;
            adjVertexNode* padjv = v.list;
            while (padjv!=NULL)
            {//!!这个算法这里破坏了原图中的入度信息。最后入度均为1
                if (--(g.VertexNode[padjv->adjVertexPosition].indegree)==0)
                    q.push(g.VertexNode[padjv->adjVertexPosition]);
                padjv = padjv->next;
            }
        }
        if (count != g.vertexNum)
            cout << "The graph has cycle, can not do topsort"<<endl;
    }
         内部的while循环最多执行|E|次,即每条边执行一次。队列对每个顶点最多执行一次操作,所以新算法的时间复杂度为O(|E|+|V|). 优于O(|V|2)因为拓扑图边数最多有n(n-1)/2,即O(|E|+|V|)<=O(|V|2)
  • 相关阅读:
    [hihocoder-1974] 智能分包 状态压缩dp
    2018北京ICPC D. Frog and Portal 斐波那契数 构造
    [hdu-6621]K-th Closest Distance 主席树 线段树 2019 多校4
    [POJ 2104]K-th Number 主席树 可持久化线段树 入门
    [hdu-6623]Minimal Power of Prime
    [hdu-6608] Fansblog 威尔逊定理 质数的密度分布 2019 多校 3
    [codeforces1000F] One Occurrence
    [python] 机器学习 卷积神经网络 用迁移学习实现人脸识别
    [python] 安装TensorFlow问题 解决Cannot uninstall 'wrapt'. It is a distutils installed project
    浅谈getResource方法
  • 原文地址:https://www.cnblogs.com/lihaozy/p/2804823.html
Copyright © 2011-2022 走看看