• [转]拓扑排序


    转自: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)
  • 相关阅读:
    NOIP2018游记
    NOIP2018T1DAY1——Road(并查集做法??)
    UVA11021 Tribles——概率dp
    捡石头——(期望递推)
    USACO2008mar-gold牛跑步(第k短路:A-star)
    Java中的异常处理
    Java学习手册
    各种应用层注入手段整理 【转载】
    正则表达式学习
    Run-Time Check Failure #0,The value of ESP was not properly saved 错误解决
  • 原文地址:https://www.cnblogs.com/lihaozy/p/2804823.html
Copyright © 2020-2023  润新知