• 算法学习记录图——应用之拓扑排序(Topological Sort)


    这一篇写有向无环图及其它的应用:

    清楚概念:

    有向无环图(DAG):一个无环的有向图。通俗的讲就是从一个点沿着有向边出发,无论怎么遍历都不会回到出发点上。

    有向无环图是描述一项工程或者系统的进行过程的有效工具,比如办公室,到工商局里面注册的时候,他会提示你一个流程,这个流程就是一个有向无环图。

    第一步不做,第二步就做不了。

    在其期间关心两个问题:

    1.工程是否顺利?(拓扑排序)

    2.估算整个工程所必须的最短时间。(关键路径)

    拓扑排序:

    数学语言:某个集合上的一个偏序得到该集合上的一个全序的操作过程。(迷糊中。。。看下离散数学。。。)

    百度百科:

    拓扑序列

    通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。离散数学中关于偏序和全序的定义:
    若集合X上的关系是R,且R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。
    设R是集合X上的偏序(Partial Order),如果对每个x,y属于X必有xRy 或 yRx,则称R是集合X上的全序关系。
    比较简单的理解:偏序是指集合中只有部分成员可以比较,全序是指集合中所有的成员之间均可以比较。
    注意:
    ①若将图中顶点按拓扑次序排成一行,则图中所有的有向边均是从左指向右的。
    ②若图中存在有向环,则不可能使顶点满足拓扑次序。
    ③一个DAG的拓扑序列通常表示某种方案切实可行。

    还是晕...

    看下面一幅图:

    这是一个偏序,1到5,可以 1-3-5 也可以是 1-2-5

    2和3没有先后,我们认为的加上 2先于3 或者 3先于2,这样的过程就将该偏序图(1,2,3,5)变成了全序图。

    我们定义1-2-3-5这个序列称为拓扑有序。而这个输出的过程就是拓扑排序。拓扑排序结果不止一种。这个图可以看到

    1-2-3-5 和1-3-2-5都是排序的结果。

    拓扑排序:就是对一个有向图构造拓扑有序的过程。

    我们把每个顶点看作是一个子过程,边表示子过程的优先顺序,这样的图我们可以定义为AOV。AOV(Activity On Vertex Network)

    如果拓扑排序:

    (1)。在有向图中选一个没有前驱的顶点且输出之。

    (2)。从图中删除该顶点和所有以它为尾的弧。

    重复上述步骤,直至全部点输出,或者当图中的顶点不存在前驱为止(有环)。

    涉及到了有向图,有前驱和后驱,这里的数据结构也要跟着改变。

    之前使用过邻接表,这里需要在邻接表的顶点信息里面添加一个入度的信息即可。

     1 #define MAXVEX 100
     2 #define IFY 65535
     3 
     4 
     5 typedef char VertexType;
     6 typedef int  EdgeType;
     7 typedef int  IdxType;
     8 typedef int QueueType;
     9 typedef int StackType;
    10 
    11 ///---------------------------------------
    12 //边节点
    13 typedef struct EdgeNode{
    14     IdxType idx;
    15     struct EdgeNode* next;
    16 }eNode;
    17 
    18 //顶点节点
    19 typedef struct VexNode{
    20     int numIn;        //入度数量
    21     IdxType idx;
    22     eNode *fitstedge;
    23 }vNode;
    24 
    25 //图的集合:包含了一个顶点数组
    26 typedef struct {
    27     vNode adjList[MAXVEX];
    28     int numVextexs,numEdges;
    29 }GraphAdjList;

    拓扑排序的代码:

     1 int TopplogicalSort(GraphAdjList *g)
     2 {
     3     int count=0;
     4     eNode *e=NULL;
     5     StackType *stack=NULL;
     6     StackType top=0;
     7     stack = (StackType *)malloc((*g).numVextexs*sizeof(StackType));
     8     int i;
     9 
    10     for (i=0;i<(*g).numVextexs;i++)
    11     {
    12         if (!(*g).adjList[i].numIn)
    13         {
    14             stack[++top] = i;
    15     //        printf("init no In is %c\n",g_init_vexs[i]);
    16         }
    17     }
    18     
    19 
    20     while(top)
    21     {
    22         int geter = stack[top];
    23         top--;
    24 
    25         printf("%c -> ",g_init_vexs[(*g).adjList[geter].idx]);
    26         count++;
    27 
    28         //获取当前点出度的点,对出度的点的入度减一(当前点要出图)。
    29         //获取当前顶点的出度点表
    30         e = (*g).adjList[geter].fitstedge;
    31         while(e)
    32         {
    33             //选取的出度点的入度减一
    34             int crntIN = --(*g).adjList[e->idx].numIn;
    35             if (crntIN == 0)
    36             {
    37                 //如果为0,则说明该顶点没有入度了,是下一轮的输出点。
    38                 stack[++top] = e->idx;
    39         //        printf("running the vex is %c\n",g_init_vexs[e->idx]);
    40             }
    41             e = e->next;
    42         }
    43     }
    44     if (count < (*g).numVextexs)//如果图本身就是一个大环,或者图中含有环,这样有环的顶点不会进栈而被打印出来。
    45     {
    46         return false;
    47     }
    48     else
    49     {
    50         printf("finish\n");
    51         return true;
    52     }
    53     
    54 }

    以下图为例做测试:

    1.找出入度为0的顶点:A、G 

     

     

     

    源代码:

      1 // grp-top-sort.cpp : 定义控制台应用程序的入口点。
      2 //
      3 
      4 #include "stdafx.h"
      5 #include <stdlib.h>
      6 
      7 
      8 #define MAXVEX 100
      9 #define IFY 65535
     10 
     11 
     12 typedef char VertexType;
     13 typedef int  EdgeType;
     14 typedef int  IdxType;
     15 typedef int QueueType;
     16 typedef int StackType;
     17 
     18 ///---------------------------------------
     19 //边节点
     20 typedef struct EdgeNode{
     21     IdxType idx;
     22     struct EdgeNode* next;
     23 }eNode;
     24 
     25 //顶点节点
     26 typedef struct VexNode{
     27     int numIn;        //入度数量
     28     IdxType idx;
     29     eNode *fitstedge;
     30 }vNode;
     31 
     32 //图的集合:包含了一个顶点数组
     33 typedef struct {
     34     vNode adjList[MAXVEX];
     35     int numVextexs,numEdges;
     36 }GraphAdjList;
     37 
     38 ///-----------------------------------
     39 VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I','J','K','L'};
     40 
     41 char *g_input[] = {
     42     "A->B->C->D",
     43     "B->E",
     44     "C->F->I->J",
     45     "D->E->I->J",
     46     "E",
     47     "F->K",
     48     "G->F->H->K",
     49     "H->I",
     50     "I->J->L",
     51     "J->E->K",
     52     "K->L",
     53     "L"
     54 };
     55 
     56 //===============================================================
     57 //队列
     58 
     59 //队列节点
     60 typedef struct Node {
     61     QueueType data;
     62     struct Node *next;
     63 }QNode,*qQNode;
     64 
     65 //队列指示
     66 typedef struct {
     67     int length;
     68     qQNode frnt,rear;    
     69 }spQueue;
     70 
     71 void init_Queue(spQueue *Q)
     72 {
     73     (*Q).frnt = NULL;
     74     (*Q).rear = NULL;
     75     (*Q).length = 0;
     76 }
     77 bool isEmptyQueue(spQueue Q)
     78 {
     79     if (Q.length == 0)
     80     {
     81         return true;
     82     }
     83     return false;
     84 }
     85 //进队
     86 void unshiftQueue(spQueue *Q,QueueType elem)
     87 {
     88     //队列空
     89     if (isEmptyQueue(*Q))
     90     {
     91         qQNode n = (qQNode)malloc(sizeof(QNode));
     92         n->data = elem;
     93         n->next = NULL;
     94 
     95         (*Q).frnt = n;
     96         (*Q).rear = n;
     97         (*Q).length = 1;
     98     }
     99     else
    100     {
    101         qQNode n = (qQNode)malloc(sizeof(QNode));
    102         n->data = elem;
    103         n->next = NULL;
    104 
    105         (*Q).rear->next = n;
    106 
    107         (*Q).rear = n;
    108         (*Q).length++;
    109     }
    110 }
    111 
    112 //出队
    113 QueueType shiftQueue(spQueue *Q)
    114 {
    115     if (isEmptyQueue(*Q))
    116     {
    117         printf("Warning:Queue is empty!!!\n");
    118         return NULL;
    119     }
    120     if ((*Q).length == 1)
    121     {
    122         QueueType sh = (*Q).frnt->data;
    123         (*Q).frnt = NULL;
    124         (*Q).rear = NULL;
    125         (*Q).length = 0;
    126         return sh;
    127     }
    128     QueueType sh = (*Q).frnt->data;
    129     (*Q).frnt = (*Q).frnt->next;
    130     (*Q).length--;
    131 
    132     return sh;
    133 }
    134 
    135 //打印队列
    136 void prt_que(spQueue que)
    137 {
    138     if (isEmptyQueue(que))
    139     {
    140         return ;
    141     }
    142     qQNode pos = que.frnt;
    143     while(que.rear->next != pos && pos != NULL)
    144     {
    145         printf(" %d ",pos->data);
    146         pos = pos->next;
    147     }
    148     printf("\n");
    149 }
    150 //===============================================================
    151 
    152 ///-------
    153 //由节点找节点的序号
    154 IdxType strFindIdx(char ch)
    155 {
    156     int i=0;
    157     VertexType *p = g_init_vexs;
    158     while(p != NULL)
    159     {
    160         if(*p == ch)
    161         {
    162             return i;
    163         }
    164         p++;
    165         i++;
    166     }
    167     return i;
    168 }
    169 
    170 //由序号找节点
    171 VertexType idxFindStr(IdxType i)
    172 {
    173     return g_init_vexs[i];
    174 }
    175 
    176 void prt_strings(char *p)
    177 {
    178     char *pos = p;
    179     while (NULL != *pos)
    180     {
    181         printf("%c",*pos);
    182         pos++;
    183     }
    184     printf("\n");
    185 }
    186 
    187 void prt_strArrays(char *p[])
    188 {
    189     char **pos = p; 
    190     while( *pos != NULL)
    191     {
    192         prt_strings(*pos);
    193         pos++;
    194     }
    195 }
    196 
    197 //我规定:顶点只能是大写。
    198 bool isVexter(char p)
    199 {
    200     if (p>='A' && p<='Z')
    201     {
    202         return true;
    203     }
    204     return false;
    205 }
    206 
    207 
    208 
    209 void init_GrapAdjList(GraphAdjList *g,char **str)
    210 {
    211     char **pos = str;
    212     int cnt=0;
    213 
    214     //入度清零
    215     int i;
    216     for (i=0;i<MAXVEX;i++)
    217     {
    218         (*g).adjList[i].numIn = 0;
    219     }
    220 
    221     while (*pos != NULL) //g_input的每行的首指针
    222     {
    223         int i=0;
    224         while(**pos != NULL) //g_input的每行字母
    225         {
    226             if(isVexter(**pos)) //判断是否为顶点(我规定‘A’-‘Z’之间为顶点标志)
    227             {
    228                 if (i == 0) //建立顶点的节点
    229                 {
    230                     (*g).adjList[cnt].idx = strFindIdx(**pos);
    231                     (*g).adjList[cnt].fitstedge = NULL;
    232                     
    233                     i=1;
    234                 }
    235                 else if(i == 1) //建立第一个边的节点
    236                 {
    237                     eNode* n = (eNode*)malloc(sizeof(eNode));
    238                     n->idx = strFindIdx(**pos);
    239                     n->next = NULL;
    240 
    241                     (*g).adjList[cnt].fitstedge = n;
    242                     i=2;
    243                     
    244                     //添加入度
    245                     int iidx = strFindIdx(**pos);
    246                     (*g).adjList[iidx].numIn++;
    247                 }
    248                 else //边节点连接到前一个边节点上
    249                 {    
    250                     eNode* n = (eNode*)malloc(sizeof(eNode));
    251                     n->idx = strFindIdx(**pos);
    252                     n->next = NULL;
    253 
    254                     //first splist
    255                     eNode *r = (*g).adjList[cnt].fitstedge;
    256                     while (r->next != NULL)
    257                     {
    258                         r = r->next;
    259                     }
    260                     r->next = n;
    261 
    262                     //添加入度
    263                     int iidx = strFindIdx(**pos);
    264                     (*g).adjList[iidx].numIn++;
    265                 }
    266             }
    267             (*pos)++; 
    268         }
    269         
    270 
    271         pos++;
    272         cnt++;
    273     }
    274     (*g).numVextexs = cnt;
    275 }
    276 
    277 int TopplogicalSort(GraphAdjList *g)
    278 {
    279     int count=0;
    280     eNode *e=NULL;
    281     StackType *stack=NULL;
    282     StackType top=0;
    283     stack = (StackType *)malloc((*g).numVextexs*sizeof(StackType));
    284     int i;
    285 
    286     for (i=0;i<(*g).numVextexs;i++)
    287     {
    288         if (!(*g).adjList[i].numIn)
    289         {
    290             stack[++top] = i;
    291     //        printf("init no In is %c\n",g_init_vexs[i]);
    292         }
    293     }
    294     
    295 
    296     while(top)
    297     {
    298         int geter = stack[top];
    299         top--;
    300 
    301         printf("%c -> ",g_init_vexs[(*g).adjList[geter].idx]);
    302         count++;
    303 
    304         //获取当前点出度的点,对出度的点的入度减一(当前点要出图)。
    305         //获取当前顶点的出度点表
    306         e = (*g).adjList[geter].fitstedge;
    307         while(e)
    308         {
    309             //选取的出度点的入度减一
    310             int crntIN = --(*g).adjList[e->idx].numIn;
    311             if (crntIN == 0)
    312             {
    313                 //如果为0,则说明该顶点没有入度了,是下一轮的输出点。
    314                 stack[++top] = e->idx;
    315         //        printf("running the vex is %c\n",g_init_vexs[e->idx]);
    316             }
    317             e = e->next;
    318         }
    319     }
    320     if (count < (*g).numVextexs)//如果图本身就是一个大环,或者图中含有环,这样有环的顶点不会进栈而被打印出来。
    321     {
    322         return false;
    323     }
    324     else
    325     {
    326         printf("finish\n");
    327         return true;
    328     }
    329     
    330 }
    331 
    332 int _tmain(int argc, _TCHAR* argv[])
    333 {
    334     GraphAdjList grp;
    335     prt_strArrays(g_input);
    336     init_GrapAdjList(&grp,g_input);
    337     if (!TopplogicalSort(&grp))
    338     {
    339         printf("grp wrong!\n");
    340     }
    341     
    342     getchar();
    343     return 0;
    344 }

    运行结果:

  • 相关阅读:
    UE4 UnLoadStreamLevel
    UE4 Xml读写
    UE4 保存为bitmap
    C++ 不定参数(转)
    C++ 默认参数(转载)
    Tuple初探
    遗传算法matlab实现
    蚁群算法matlab实现
    汉诺塔问题C++实现
    较优H圈matlab实现
  • 原文地址:https://www.cnblogs.com/jsgnadsj/p/3432809.html
Copyright © 2020-2023  润新知