博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[转]拓扑排序
阅读量:5235 次
发布时间:2019-06-14

本文共 4022 字,大约阅读时间需要 13 分钟。

转自:

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

在现实生活中,也会有不少应用例子,比如学校课程布置图,要先修完一些基础课,才可以继续修专业课。
一个简单的求拓扑排序的算法:首先要找到任意入度为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 = 0;  i < g . vertexNum;  i ++)
     {
        
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)

转载于:https://www.cnblogs.com/lihaozy/archive/2012/12/06/2804823.html

你可能感兴趣的文章
Luogu[SDOI2008]Sue的小球
查看>>
linux安装-mysql5.7.24
查看>>
linux网络配置、环境变量以及JDK安装(CentOS 6.5)
查看>>
Leetcode Validate Binary Search Tree
查看>>
利用套打和分栏巧妙来做商品价签
查看>>
hdu 1102 Constructing Roads(kruskal || prim)
查看>>
JS中一些特殊的方法
查看>>
vb.net版机房收费——助你学会七层架构(二)反射+抽象工厂
查看>>
【转载】SpringBoot yml 配置
查看>>
tcp client.cs
查看>>
函数 2018-3-26
查看>>
C#调用C++函数的注意事项
查看>>
类别联动下拉菜单
查看>>
程序的存储空间布局/堆与栈
查看>>
ubuntu环境变量设置
查看>>
【LOJ】#2061. 「HAOI2016」放棋子
查看>>
USACO 5.3 Network of Schools
查看>>
【LOJ】#2494. 「AHOI / HNOI2018」寻宝游戏
查看>>
JS检测数据类型
查看>>
学会使用自定义选择器
查看>>