这一篇写有向无环图及其它的应用:
清楚概念:
有向无环图(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 }
运行结果: