這一篇寫有向無環圖及其它的應用:數組
清楚概念:數據結構
有向無環圖(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; }
運行結果: