这一篇写有向无环图及其它的应用:数组
清楚概念:数据结构
有向无环图(DAG):一个无环的有向图。通俗的讲就是从一个点沿着有向边出发,不管怎么遍历都不会回到出发点上。工具
有向无环图是描述一项工程或者系统的进行过程的有效工具,好比办公室,到工商局里面注册的时候,他会提示你一个流程,这个流程就是一个有向无环图。测试
第一步不作,第二步就作不了。spa
在其期间关心两个问题:3d
1.工程是否顺利?(拓扑排序)指针
2.估算整个工程所必须的最短期。(关键路径)blog
拓扑排序:排序
数学语言:某个集合上的一个偏序获得该集合上的一个全序的操做过程。队列
百度百科:
拓扑序列
一般,这样的线性序列称为知足拓扑次序(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)从图中删除该顶点和全部以它为尾的弧
重复上述步骤,直至所有点输出,或者当图中的顶点不存在前驱为止(有环)。
涉及到了有向图,有前驱和后驱,这里的数据结构也要跟着改变。
以前使用过邻接表,这里须要在邻接表的顶点信息里面添加一个入度的信息便可。
#define MAXVEX 100 #define IFY 65535 typedef char VertexType; typedef int EdgeType; typedef int IdxType; typedef int QueueType; typedef int StackType; ///--------------------------------------- //边节点 typedef struct EdgeNode{ IdxType idx; struct EdgeNode* next; }eNode; //顶点节点 typedef struct VexNode{ int numIn; //入度数量 IdxType idx; eNode *fitstedge; }vNode; //图的集合:包含了一个顶点数组 typedef struct { vNode adjList[MAXVEX]; int numVextexs,numEdges; }GraphAdjList;
拓扑排序的代码:
int TopplogicalSort(GraphAdjList *g) { int count=0; eNode *e=NULL; StackType *stack=NULL; StackType top=0; stack = (StackType *)malloc((*g).numVextexs*sizeof(StackType)); int i; for (i=0;i<(*g).numVextexs;i++) { if (!(*g).adjList[i].numIn) { stack[++top] = i; // printf("init no In is %c\n",g_init_vexs[i]); } } while(top) { int geter = stack[top]; top--; printf("%c -> ",g_init_vexs[(*g).adjList[geter].idx]); count++; //获取当前点出度的点,对出度的点的入度减一(当前点要出图)。 //获取当前顶点的出度点表 e = (*g).adjList[geter].fitstedge; while(e) { //选取的出度点的入度减一 int crntIN = --(*g).adjList[e->idx].numIn; if (crntIN == 0) { //若是为0,则说明该顶点没有入度了,是下一轮的输出点。 stack[++top] = e->idx; // printf("running the vex is %c\n",g_init_vexs[e->idx]); } e = e->next; } } if (count < (*g).numVextexs)//若是图自己就是一个大环,或者图中含有环,这样有环的顶点不会进栈而被打印出来。 { return false; } else { printf("finish\n"); return true; } }
如下图为例作测试:
找出入度为0的顶点:A、G
源代码:
// grp-top-sort.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdlib.h> #define MAXVEX 100 #define IFY 65535 typedef char VertexType; typedef int EdgeType; typedef int IdxType; typedef int QueueType; typedef int StackType; ///--------------------------------------- //边节点 typedef struct EdgeNode{ IdxType idx; struct EdgeNode* next; }eNode; //顶点节点 typedef struct VexNode{ int numIn; //入度数量 IdxType idx; eNode *fitstedge; }vNode; //图的集合:包含了一个顶点数组 typedef struct { vNode adjList[MAXVEX]; int numVextexs,numEdges; }GraphAdjList; ///----------------------------------- VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I','J','K','L'}; char *g_input[] = { "A->B->C->D", "B->E", "C->F->I->J", "D->E->I->J", "E", "F->K", "G->F->H->K", "H->I", "I->J->L", "J->E->K", "K->L", "L" }; //=============================================================== //队列 //队列节点 typedef struct Node { QueueType data; struct Node *next; }QNode,*qQNode; //队列指示 typedef struct { int length; qQNode frnt,rear; }spQueue; void init_Queue(spQueue *Q) { (*Q).frnt = NULL; (*Q).rear = NULL; (*Q).length = 0; } bool isEmptyQueue(spQueue Q) { if (Q.length == 0) { return true; } return false; } //进队 void unshiftQueue(spQueue *Q,QueueType elem) { //队列空 if (isEmptyQueue(*Q)) { qQNode n = (qQNode)malloc(sizeof(QNode)); n->data = elem; n->next = NULL; (*Q).frnt = n; (*Q).rear = n; (*Q).length = 1; } else { qQNode n = (qQNode)malloc(sizeof(QNode)); n->data = elem; n->next = NULL; (*Q).rear->next = n; (*Q).rear = n; (*Q).length++; } } //出队 QueueType shiftQueue(spQueue *Q) { if (isEmptyQueue(*Q)) { printf("Warning:Queue is empty!!!\n"); return NULL; } if ((*Q).length == 1) { QueueType sh = (*Q).frnt->data; (*Q).frnt = NULL; (*Q).rear = NULL; (*Q).length = 0; return sh; } QueueType sh = (*Q).frnt->data; (*Q).frnt = (*Q).frnt->next; (*Q).length--; return sh; } //打印队列 void prt_que(spQueue que) { if (isEmptyQueue(que)) { return ; } qQNode pos = que.frnt; while(que.rear->next != pos && pos != NULL) { printf(" %d ",pos->data); pos = pos->next; } printf("\n"); } //=============================================================== ///------- //由节点找节点的序号 IdxType strFindIdx(char ch) { int i=0; VertexType *p = g_init_vexs; while(p != NULL) { if(*p == ch) { return i; } p++; i++; } return i; } //由序号找节点 VertexType idxFindStr(IdxType i) { return g_init_vexs[i]; } void prt_strings(char *p) { char *pos = p; while (NULL != *pos) { printf("%c",*pos); pos++; } printf("\n"); } void prt_strArrays(char *p[]) { char **pos = p; while( *pos != NULL) { prt_strings(*pos); pos++; } } //我规定:顶点只能是大写。 bool isVexter(char p) { if (p>='A' && p<='Z') { return true; } return false; } void init_GrapAdjList(GraphAdjList *g,char **str) { char **pos = str; int cnt=0; //入度清零 int i; for (i=0;i<MAXVEX;i++) { (*g).adjList[i].numIn = 0; } while (*pos != NULL) //g_input的每行的首指针 { int i=0; while(**pos != NULL) //g_input的每行字母 { if(isVexter(**pos)) //判断是否为顶点(我规定‘A’-‘Z’之间为顶点标志) { if (i == 0) //创建顶点的节点 { (*g).adjList[cnt].idx = strFindIdx(**pos); (*g).adjList[cnt].fitstedge = NULL; i=1; } else if(i == 1) //创建第一个边的节点 { eNode* n = (eNode*)malloc(sizeof(eNode)); n->idx = strFindIdx(**pos); n->next = NULL; (*g).adjList[cnt].fitstedge = n; i=2; //添加入度 int iidx = strFindIdx(**pos); (*g).adjList[iidx].numIn++; } else //边节点链接到前一个边节点上 { eNode* n = (eNode*)malloc(sizeof(eNode)); n->idx = strFindIdx(**pos); n->next = NULL; //first splist eNode *r = (*g).adjList[cnt].fitstedge; while (r->next != NULL) { r = r->next; } r->next = n; //添加入度 int iidx = strFindIdx(**pos); (*g).adjList[iidx].numIn++; } } (*pos)++; } pos++; cnt++; } (*g).numVextexs = cnt; } int TopplogicalSort(GraphAdjList *g) { int count=0; eNode *e=NULL; StackType *stack=NULL; StackType top=0; stack = (StackType *)malloc((*g).numVextexs*sizeof(StackType)); int i; for (i=0;i<(*g).numVextexs;i++) { if (!(*g).adjList[i].numIn) { stack[++top] = i; // printf("init no In is %c\n",g_init_vexs[i]); } } while(top) { int geter = stack[top]; top--; printf("%c -> ",g_init_vexs[(*g).adjList[geter].idx]); count++; //获取当前点出度的点,对出度的点的入度减一(当前点要出图)。 //获取当前顶点的出度点表 e = (*g).adjList[geter].fitstedge; while(e) { //选取的出度点的入度减一 int crntIN = --(*g).adjList[e->idx].numIn; if (crntIN == 0) { //若是为0,则说明该顶点没有入度了,是下一轮的输出点。 stack[++top] = e->idx; // printf("running the vex is %c\n",g_init_vexs[e->idx]); } e = e->next; } } if (count < (*g).numVextexs)//若是图自己就是一个大环,或者图中含有环,这样有环的顶点不会进栈而被打印出来。 { return false; } else { printf("finish\n"); return true; } } int _tmain(int argc, _TCHAR* argv[]) { GraphAdjList grp; prt_strArrays(g_input); init_GrapAdjList(&grp,g_input); if (!TopplogicalSort(&grp)) { printf("grp wrong!\n"); } getchar(); return 0; }
运行结果: