多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > 圖的深度優先搜索及拓撲排序

圖的深度優先搜索及拓撲排序

來源:程序員人生   發布時間:2016-06-21 08:00:50 閱讀次數:3826次

本文將介紹圖的深度優先搜索,并實現基于深度優先搜索的拓撲排序(拓撲排序適用于有向無環圖,下面詳細介紹)。

1. 圖的深度優先遍歷要解決的問題

圖的深度優先搜索與樹的深度優先搜索類似,但是對圖進行深度優先搜索要解決1個問題,那就是頂點的重復訪問,假定圖中存在1個環路A-B-C-A,那末對頂點A進行展開后得到B,對B進行展開后得到C,然后對C進行展開后得到A,然后A就被重復訪問了。。。

這明顯是不對的!我們需要用1個狀態變量來記錄1個頂點被訪問和被展開的狀態。在《算法導論》中,作者使用3種色彩來對此進行標記:WHITE——頂點還沒被訪問過,GRAY——頂點已被訪問過但是其子結點還沒被訪問完,BLACK——頂點已被訪問過而且其子結點已被訪問完了。

2. 用棧實現圖的深度優先遍歷

在《算法導論》中,作者使用了遞歸來實現深度優先遍歷。雖然遞歸寫起來更加簡潔而且容易理解,但是我其實不建議在實際工程中這樣做,除非你的問題范圍比較小。否則遞歸會讓你的程序萬劫不復。正如使用隊列實現廣度優先搜索1樣(可以看到我的上1篇博客圖的廣度優先遍歷),下面我將會使用棧來實現深度優先搜索。

下面用1個例子來講明如何使用棧來實現深度優先遍歷。假定有下面1個圖。

首先,我們初始化1個空棧,然后將所有頂點入棧,此時所有頂點都沒有被訪問過,因此所有頂點都置為白色,以下所示。


上述初始化完成以后,頂點5位于棧底,頂點1位于棧頂,然后我們就進入循環操作,首先出棧1個頂點,該頂點為頂點1,由于頂點1是白色的,即沒有被訪問過,因而就將頂點1置為灰色,并重新入棧(由于下面要記錄結點的子結點被訪問完的次序),然后展開頂點1的所有頂點,只有頂點2,由于頂點2是白色即未被訪問過,因而將頂點2入棧,以下圖所示。


然后我們繼續循環,出棧1個頂點,即頂點2,由于頂點2是白色的,因而將其置為灰色,并重新入棧,然后展開得到頂點4和頂點5,由于頂點4和頂點5均為白色,因而將它們都入棧。以下圖所示。


然后還是繼續循環,出棧1個頂點,即頂點5,由于頂點5是白色的,因而將其置為灰色,并重新入棧,然后展開頂點5,發現頂點5沒有子結點,以下圖所示。


然后繼續循環,出棧頂點5,此時頂點5為灰色,說明頂點5的子結點已被訪問完了,因而將頂點5置為黑色,并將其次序標記為1,說明他是第1個被展開完的結點,這1個記錄將會被利用到拓撲排序中。此時棧的狀態以下所示。


繼續循環,出棧頂點4,由于是白色的,所以將頂點4置為灰色并重新入棧,然后展開頂點4得到頂點5,由于頂點5是黑色的,因而不將其入棧。接下來繼續出棧頂點4,發現頂點4是灰色的,因而和前面1樣,將頂點4置為黑色并將其次序記錄為2。此時的棧以下圖所示。

好了,后面的循環的工作也跟上述類似,順次將頂點2、1出棧并置為黑色,直到頂點3展開發現沒有白色的子結點,也將其置為黑色。然后再到了頂點4和頂點5,由于是黑色,直接出棧不作任何處理了。此時棧為空,遍歷結束。

我們的程序需要的輸入的圖以鄰接表的情勢表示,下面給出圖及頂點和鄰接表的定義。

typedef enum VertexColor { Vertex_WHITE = 0, // 未被搜索到 Vertex_BLACK = 1, // 子結點都被搜索終了 Vertex_GRAY = 2 // 子結點正在被搜索 } VertexColor; typedef struct GNode { int number; // 頂點編號 struct GNode *next; } GNode; typedef struct Vertex { int number; int f; VertexColor color; // 搜索進程標記搜索狀態 struct Vertex *p; } Vertex; typedef struct Graph { GNode *LinkTable; Vertex *vertex; int VertexNum; } Graph;
VertexColor是頂點的色彩枚舉量定義。GNode是鄰接表的元素定義,其記錄了頂點的編號。鄰接表本質上就是1個指針數組,其每一個元素都是指向1個鏈表的第1個元素的指針。Vertex是頂點的數據結構,其屬性number為頂點編號,f是記錄其被訪問完的次序,color是狀態標識色彩,p指向搜索完成后得到的深度優先遍歷樹中的頂點的先驅。Graph是圖的數據結構定義,其包括了1個頂點數組,按編號升序排列,LinkTable是鄰接表。

下面給出用棧實現的深度優先搜索的程序。

/** * 深度優先搜索,要求輸入圖g的結點編號從1開始 */ void searchByDepthFirst(Graph *g) { int VertexNum = g->VertexNum; Stack *stack = initStack(); Vertex *vs = g->vertex; GNode *linkTable = g->LinkTable; int order = 0; for (int i = 0; i < VertexNum; i++) { Vertex *v = vs + i; v->color = Vertex_WHITE; v->p = NULL; push(&stack, v->number); } while (!isEmpty(stack)) { int number = pop(&stack); Vertex *u = vs + number - 1; if (u->color == Vertex_WHITE) { // 開始搜索該結點的子結點 u->color = Vertex_GRAY; push(&stack, number); } else if (u->color == Vertex_GRAY) { // 該結點的子結點已被搜索完了 u->color = Vertex_BLACK; u->f = order++; continue; } else { continue; } GNode *links = linkTable + number - 1; links = links->next; while (links != NULL) { // 展開子結點并入棧 Vertex *v = vs + links->number - 1; if (v->color == Vertex_WHITE) { v->p = u; push(&stack, links->number); } links = links->next; } } }
程序先將所有頂點初始化為白色并入棧,然后開始循環。在循環中,每次出棧1個結點都要先對其色彩進行判斷,如果是灰色,標記其被訪問完成的次序并標為黑色,如果是黑色,不作任何處理,如果是白色,則置為灰色并重新入棧,然后展開其所有子結點并將白色的子結點入棧,并且記下這些白色頂點的先驅。當棧為空時,循環結束。

棧的操作可以參考其它資料,這里不做詳述,下面給出1個簡單實現。

typedef struct Stack { int value; struct Stack *pre; } Stack;
上面是棧的結構定義。

Stack* initStack() { Stack *s = (Stack *)malloc(sizeof(Stack)); s->pre = NULL; return s; } void push(Stack **s, int value) { Stack *n = (Stack *)malloc(sizeof(Stack)); n->pre = *s; n->value = value; *s = n; } int pop(Stack **s) { if ((*s)->pre == NULL) { return INT_MAX; } int value = (*s)->value; Stack *pre = (*s)->pre; free(*s); *s = pre; return value; } int isEmpty(Stack *s) { if (s->pre == NULL) { return 1; } return 0; } void destroyStack(Stack **s) { while (*s != NULL) { Stack *pre = (*s)->pre; free(*s); *s = pre; } }
上面是棧的方法,順次是初始化1個空棧,入棧,出棧,判斷是不是為空棧,燒毀棧。

下面給出1個利用上述深度優先遍歷方法的例子代碼和運行結果。

Graph graph; graph.VertexNum = 5; Vertex v[5]; Vertex v1; v1.number = 1; v1.p = NULL; v[0] = v1; Vertex v2; v2.number = 2; v2.p = NULL; v[1] = v2; Vertex v3; v3.number = 3; v3.p = NULL; v[2] = v3; Vertex v4; v4.number = 4; v4.p = NULL; v[3] = v4; Vertex v5; v5.number = 5; v5.p = NULL; v[4] = v5; graph.vertex = v; GNode nodes[5]; GNode n1; n1.number = 1; GNode n2; n2.number = 2; GNode n3; n3.number = 3; GNode n4; n4.number = 4; GNode n5; n5.number = 5; GNode y; y.number = 5; GNode e; e.number = 2; GNode f; f.number = 5; GNode g; g.number = 2; GNode h; h.number = 4; n1.next = &e; e.next = NULL; n2.next = &h; h.next = &f; f.next = NULL; n3.next = &g; g.next = NULL; n4.next = &y; y.next = NULL; n5.next = NULL; nodes[0] = n1; nodes[1] = n2; nodes[2] = n3; nodes[3] = n4; nodes[4] = n5; graph.LinkTable = nodes; searchByDepthFirst(&graph); printf("\n"); printPath(&graph, 2);
運行結果以下。

上述示例代碼對本文前面給出的圖進行深度優先遍歷后,輸出頂點2的先驅子樹,得到上述結果,意思是頂點2的先驅頂點為3。

3. 圖的拓撲排序

圖的拓撲排序可以利用深度優先遍歷來實現。

首先,說明1下甚么是拓撲排序。拓撲排序是指將圖按前后順序組織排列起來,前后順序是指在排序結果中,前面的頂點多是有1條簡單路徑通向后面的頂點的,而后面的頂點是肯定沒有1條簡單路徑通向前面的頂點的。拓撲排序適用于有向無環圖。下面給出1個拓撲排序的例子。

還是這個圖,這也是1個有向無環圖,拓撲排序的結果最前面的頂點肯定是1個根頂點,即沒有其它頂點指向他。這個圖的拓撲排序結果是1、3、2、4、5,或1、3、2、5、4,等等,不存在指向關系的頂點間的順序是不肯定的。我們可以將拓撲排序的圖看成是1個日程表,頂點1代表洗臉,頂點3代表刷牙,頂點2代表吃早飯,頂點4代表穿褲子,頂點5代表穿衣服,因而拓撲排序本質就是根據事件應當產生的前后順序組織起來。

圖的深度優先遍歷有1個特點,那就是,當1個頂點的子結點都被訪問完了,該頂點才會結束訪問,并開始向上回溯訪問它的父結點的其它子結點。這意味著,1個頂點的結束訪問時間與其子結點的結束訪問時間存在前后關系,而這個順序恰好與拓撲排序是相反的!簡單地說,在深度優先遍歷中,頂點A要結束訪問的條件是其子結點B、C、D...都被訪問完了,而在拓撲排序中,事件A完成以后其后面的事件B、C、D...才可以繼續進行。這也就是上面的深度優先搜索為何要記錄結點被訪問完成的次序,由于這個次序倒過來就是拓撲排序的順序!

下面給出基于圖的深度優先搜索實現的拓撲排序的程序。

/** * 有向無環圖的拓撲排序 */ void topologySort(Graph *g, int **order, int *n) { searchByDepthFirst(g); *n = g->VertexNum; *order = (int *)malloc(sizeof(int) * *n); for (int i = 0; i < *n; i++) { (*order)[*n - 1 - g->vertex[i].f] = i + 1; } }
一樣的,上述程序實現的拓撲排序要求的圖的頂點編號也是從1開始。

其實拓撲排序還有另外一種實現方法,那就是從根頂點開始,刪除所有根頂點及與之相連的邊。此時就會產生新的根頂點,然后繼續重復上述操作,知道所有頂點都被刪除。

4. 總結

本文介紹了如何使用棧來實現圖的深度優先搜索,并基于圖的深度優先搜索實現了拓撲排序。其時間復雜度均為O(n)。完全程序可以參考我的github項目數據結構與算法

這個項目里面有本博客介紹過的和沒有介紹的和將要介紹的《算法導論》中部份主要的數據結構和算法的C實現,有興趣的可以fork或star1下哦~ 由于本人還在研究《算法導論》,所以這個項目還會延續更新哦~ 大家1起好好學習~

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 亚洲人成免费 | 都市激情校园春色亚洲 | 秋霞伊人网 | 亚洲福利在线观看 | 国产片在线看 | 亚洲欲色 | 麻豆亚洲 | 一区二区三区免费视频播放器 | 国产欧美日韩精品高清二区综合区 | 鲁在线| 精品久久久久久久 | 视频网站免费观看 | 久久精品www | 亚洲视频免费观看 | 最新亚洲精品国自产在线观看 | 337p欧洲日本大胆艺术 | 国产视频h| 日韩麻豆 | 久久亚洲人成国产精品 | 在线观看 亚洲 | 激情五月婷婷网 | 中文字幕www| 图片区小说校园 | 2020国产精品自拍 | 国产人成亚洲第一网站在线播放 | 综合亚洲一区二区三区 | 中文字幕一区二区三区精彩视频 | 国产福利乳摇在线播放 | 婷婷久久综合 | 自拍偷拍亚洲区 | 四虎一区二区三区精品 | 国产麻豆精品在线观看 | 欧美激情一级欧美精品 | 亚洲三级在线视频 | 最新欧美精品一区二区三区 | 国产午夜精品一区二区三区 | 亚洲高清一区二区三区四区 | 最新中文字幕一区二区乱码 | 伊人国产精品 | 久久久国产一区二区三区 | 日韩欧美精品中文字幕 |