算法学习记录-图——应用之拓扑排序(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 }

 

运行结果:

 

 

 

posted @ 2013-11-25 12:18  sjdang  阅读(2409)  评论(0编辑  收藏  举报