zoukankan      html  css  js  c++  java
  • 浅谈数据结构-拓扑排序

    在生活、工作中进行一项任务,必须有先后顺序,这就是流程图,在流程图中有if分支,也就是下一个活动的展开,可能上一次活动中所有分支执行完毕,这样就形成了拓扑结构。接入项目中比较简单,我们可以通过链表表示项目之间各个活动的关系,当项目流程比较复杂时,我们需要有向图来描述这种关系,而且是不能循环的有向图,我们称之为有向无环图。

    在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的有限关系,这样的有向图为顶点表是活动的图,成为AOV网(Activity On Vertex Network)。在AOV网中弧表示活动之间的某种约束关系。

    一个AOV网应该是一个有向无环图,即不应该带有回路,因为若带有回路,则回路上的所有活动都无法进行。如图3-6是一个具有三个顶点的回路,由<A,B>边可得B活动必须在A活动之后,由<B,C>边可得C活动必须在B活动之后,所以推出C活动必然在A活动之后,但由<C,A>边可得C活动必须在A活动之前,从而出现矛盾,使每一项活动都无法进行。这种情况若在程序中出现,则称为死锁或死循环,是应该必须避免的。

        在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。

    一、算法思想

    从AOV网中选取一个入度为0的顶点输出,然后删除此顶点,并删除以此顶点为尾的弧,继续重复此操作,直到输出全部顶点或者AOV网中不存在入度为0的顶点为止。 在左图中入度为0的点位c1和c2,如按照拓扑排序算法进行运算结果为:

    (1) C1,C8,C9,C2,C3,C5,C4,C7,C6

    (2) C2,C1,C3,C5,C4,C6,C8,C9,C7

    (3) C1,C2,C3,C8,C9,C5,C4,C6,C7

        由AOV网构造出拓扑序列的实际意义是:如果按照拓扑序列中的顶点次序,在开始每一项活动时,能够保证它的所有前驱活动都已完成,从而使整个工程顺序进行,不会出现冲突的情况。

    二、算法分析

    1. 从AOV网络中删除入度为0的顶点;有入度这个概念,则用链表的存储结构储存数据。
      //边表结点
      typedef struct EdgeNode
      {
          int nNodevex;     //邻接点的点表中结点的坐标
          EdgeType nNodeWeight;   //用于网图中边的权值
          EdgeNode* next;       //链域,指向下一个邻接点
      }EdgeNode,*pEdgeNode;
      
      typedef struct VertexNodeAdv
      {
          int in;                  //顶点的度
          VertexType nNodeData;   //顶点表中存储的数据
          pEdgeNode pFirstNode;   //顶点表和边表中关联指针,指向边表头指针
      }VertexNodeAdv,*pVertexNodeAdv,VertexListAdv[MAXVEX];;
      
      
      //图结构
      typedef struct
      {
          VertexListAdv vertexList;
          int numVertess,numEdges;
      }GraphListAdv;
      存储结构
    2. 删除此顶点还有顶点尾的弧;此时删除顶点(输出数据),删除顶点为尾的弧,意味这弧消失,图中一个顶点的入度减1.必须在顶点表中有表示度的变量。
    3. 继续循环,直到不存在度为0的顶点;就是循环停止条件是顶点全部输出。在程序中用堆栈图中顶点。
    4. 入度为0的顶点全部入栈,进入循环后,一顶点出栈,获取这顶点中连接的下一个顶点。

    三、例图解释

    (1) 在(a)图中v0和v1的入度都为0,不妨选择v0并输出之,接着删去顶点v0及出边<0,2>,得到的结果如(b)图所示。

    (2) 在(b)图中只有一个入度为0的顶点v1,输出v1,接着删去v1和它的三条出边<1,2>,<1,3>和<1,4>,得到的结果如(c)图所示。

    (3) 在(c)图中v2和v4的入度都为0,不妨选择v2并输出之,接着删去v2及两条出边<2,3>和<2,5>,得到的结果如(d)图所示。

    (4) 在(d)图上依次输出顶点v3,v4和v5,并在每个顶点输出后删除该顶点及出边,操作都很简单,不再赘述。

    为了利用C++语言在计算机上实现拓扑排序算法,AOV网采用邻接表表示较方便。

    四、代码

    int GraphData::GetGraphAdvListLocation(GraphListAdv* pList,char chpoint)
    {
        GraphListAdv *pGraphList = pList;
        int i = 0;
        for (i = 0;i< pGraphList->numVertess;i++)
        {
            if (pGraphList->vertexList[i].nNodeData == chpoint)
            {
                break;;
            }
        }
        if (i >= pGraphList->numVertess)
        {
            return -1;
        }
        return i;
    }
    void GraphData::CreateGraphListAdv(GraphListAdv* pList,int numVer,int numEdegs)
    {
        int weight = 0;
        GraphListAdv *pGraphList = pList;
        pGraphList->numVertess = numVer;
        pGraphList->numEdges = numEdegs;
        EdgeNode* firstNode,*secondNode;
        //创建顶点表
        for (int i= 0; i < numVer;i++)
        {
            pGraphList->vertexList[i].nNodeData = getchar();
            pGraphList->vertexList[i].pFirstNode = NULL;
            while(pGraphList->vertexList[i].nNodeData == '
    ')
            {
                pGraphList->vertexList[i].nNodeData = getchar();
            }
        }
    
        //创建边表    
        for(int k = 0; k < pGraphList->numEdges; k++)
        {
            char p, q;
            printf("输入边(vi,vj)上的下标i,下标j和权值:
    ");
    
            p = getchar();
            while(p == '
    ')
            {
                p = getchar();
            }
            q = getchar();
            while(q == '
    ')
            {
                q = getchar();
            }
            scanf("%d", &weight);    
    
            int m = -1;
            int n = -1;
            m = GetGraphAdvListLocation(pGraphList, p);
            n = GetGraphAdvListLocation(pGraphList, q);
            if(n == -1 || m == -1)
            {
                fprintf(stderr, "there is no this vertex.
    ");
                return;
            }
            //getchar();
            //字符p在顶点表的坐标为m,与坐标n的结点建立联系权重为weight
            firstNode = new EdgeNode();
            firstNode->nNodevex = n;
            firstNode->next = pGraphList->vertexList[m].pFirstNode;
            pGraphList->vertexList[n].in++;
            firstNode->nNodeWeight = weight;
            pGraphList->vertexList[m].pFirstNode = firstNode;
    
            ////第二个字符second
            //secondNode = new EdgeNode();
            //secondNode->nNodevex = m;
            //secondNode->next = pGraphList->vertexList[n].pFirstNode;
            //pGraphList->vertexList[n].in++;
            //secondNode->nNodeWeight = weight;
            //pGraphList->vertexList[n].pFirstNode = secondNode;
    
        }
    }
    void GraphData::TopoLogicalSort(GraphListAdv *pList)
    {
        EdgeNode *e;
        int ncount = 0;  //统计输出顶点个数,用于判断是否有环
        int nIndex ; // y用于保存度为0的坐标
        stack<int> staNode;
        for (int i = 0;i < pList->numVertess; i++)
        {
            if (pList->vertexList[i].in == 0)
            {
                staNode.push(i);    //将入度为0的顶点入栈
            }
        }
        while(!staNode.empty())
        {
            nIndex = staNode.top();  // 获取入度为0的顶点
            staNode.pop();     //出栈
            printf("%c ->",pList->vertexList[nIndex].nNodeData);  //打印顶点
            ncount ++;
            //对此顶点的处理:下一个顶点入度减1,如果为0还要进栈
            for (e = pList->vertexList[nIndex].pFirstNode;e;e = e->next)
            {
                //对此顶点弧表遍历
                int temp = e->nNodevex;
                //将temp号顶点邻接点的入度减1,若为0,则入栈,以便于下次循环输出
                if (!(--pList->vertexList[temp].in))
                {
                    staNode.push(temp);
                }
            }
        }
        if (ncount < pList->numVertess)
        {
            printf(" 图有内环");
        }
    
    }
    拓扑程序

    将例图解释中的图输入到程序中,其中0对应a,1对应b,等输入其中,进行拓扑排序输出。拓扑排序输出类似广度遍历。

    首先将入度为0的顶点压入堆栈;堆栈为0,1.(a,b)

    判断堆栈是否为空,不为空,压出;此时为1.(b),输出。

    获取顶点1的弧尾顶点,有4,3,2,入度分别减1,入度为0的入栈,4入栈(e)。

    4(e)出栈,输出,继续循环。

    image

  • 相关阅读:
    实习小白::(转) 官网文档 -->> 骨骼动画详解-Spine
    实习小白::(转) 在cocos2d-x中使用CCArmature实现骨骼动画
    实习小白::(转) 骨骼动画 vs. 精灵表(sprite sheets)
    实习小白::(转) Cocos2d-x 更改文字换行风格 ( cocos2dx change line )
    实习小白::(转) Cocos2d-x 3.x 开发(十八)10行代码看自动Batch,10行代码看自动剔除 --------其实就是渲染图片机制
    实习小白::(转) Cocos2d-x 3.0 开发(十七)使用Cocos2d-x 3.0rc,一条命令打包Android
    实习小白::(转) Cocos2d-x 3.0 开发(十六)cocos2dx-3.0beta版建立新项目并加载CocoStudio导出文件
    实习小白::(转) Cocos2d-x 3.0 开发(十五)使用UILayout布局,制作对话界面
    实习小白::(转) Cocos2d-x 3.0 开发(十四)使用UIScrollView 实现大小不同物品拖动展示
    实习小白::(转) Cocos2d-x 3.0开发(十三)使用CocoStudio编辑帧事件并关联到程序
  • 原文地址:https://www.cnblogs.com/polly333/p/4778372.html
Copyright © 2011-2022 走看看