日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

BFS和DFS优先搜索算法

發布時間:2025/6/15 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 BFS和DFS优先搜索算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

4、教你通透徹底理解:BFS和DFS優先搜索算法

?

?作者:July??二零一一年一月一日

---------------------------------

本人參考:算法導論?
本人聲明:個人原創,轉載請注明出處。

ok,開始。

翻遍網上,關于此類BFS和DFS算法的文章,很多。但,都說不出個所以然來。
讀完此文,我想,
你對圖的廣度優先搜索和深度優先搜索定會有個通通透透,徹徹底底的認識。

---------------------

咱們由BFS開始:
首先,看下算法導論一書關于 此BFS 廣度優先搜索算法的概述。
算法導論第二版,中譯本,第324頁。
廣度優先搜索(BFS)
在Prime最小生成樹算法,和Dijkstra單源最短路徑算法中,都采用了與BFS 算法類似的思想。

//u 為 v 的先輩或父母。
BFS(G, s) 1 for each vertex u ∈ V [G] - {s}2 do color[u] ← WHITE3 d[u] ← ∞4 π[u] ← NIL//除了源頂點s之外,第1-4行置每個頂點為白色,置每個頂點u的d[u]為無窮大,//置每個頂點的父母為NIL。 5 color[s] ← GRAY//第5行,將源頂點s置為灰色,這是因為在過程開始時,源頂點已被發現。 6 d[s] ← 0 //將d[s]初始化為0。 7 π[s] ← NIL //將源頂點的父頂點置為NIL。 8 Q ← ?9 ENQUEUE(Q, s) //入隊//第8、9行,初始化隊列Q,使其僅含源頂點s。 10 while Q ≠ ? 11 do u ← DEQUEUE(Q) //出隊//第11行,確定隊列頭部Q頭部的灰色頂點u,并將其從Q中去掉。 12 for each v ∈ Adj[u] //for循環考察u的鄰接表中的每個頂點v 13 do if color[v] = WHITE 14 then color[v] ← GRAY //置為灰色 15 d[v] ← d[u] + 1 //距離被置為d[u]+1 16 π[v] ← u //u記為該頂點的父母 17 ENQUEUE(Q, v) //插入隊列中 18 color[u] ← BLACK //u 置為黑色

由下圖及鏈接的演示過程,清晰在目,也就不用多說了:?

廣度優先遍歷演示地址: http://sjjg.js.zwu.edu.cn/SFXX/sf1/gdyxbl.html -----------------------------------------------------------------------------------------------------------------
ok,不再贅述。接下來,具體講解深度優先搜索算法。
深度優先探索算法 DFS?
//u 為 v 的先輩或父母。
DFS(G) 1 for each vertex u ∈ V [G] 2 do color[u] ← WHITE 3 π[u] ← NIL //第1-3行,把所有頂點置為白色,所有π 域被初始化為NIL。 4 time ← 0 //復位時間計數器 5 for each vertex u ∈ V [G] 6 do if color[u] = WHITE 7 then DFS-VISIT(u) //調用DFS-VISIT訪問u,u成為深度優先森林中一棵新的樹//第5-7行,依次檢索V中的頂點,發現白色頂點時,調用DFS-VISIT訪問該頂點。//每個頂點u 都對應于一個發現時刻d[u]和一個完成時刻f[u]。 DFS-VISIT(u) 1 color[u] ← GRAY //u 開始時被發現,置為白色 2 time ← time +1 //time 遞增 3 d[u] <-time //記錄u被發現的時間 4 for each v ∈ Adj[u] //檢查并訪問 u 的每一個鄰接點 v 5 do if color[v] = WHITE //如果v 為白色,則遞歸訪問v。 6 then π[v] ← u //置u為 v的先輩 7 DFS-VISIT(v) //遞歸深度,訪問鄰結點v 8 color[u] <-BLACK //u 置為黑色,表示u及其鄰接點都已訪問完成 9 f [u] ? time ← time +1 //訪問完成時間記錄在f[u]中。 // 第1-3行,5-7行循環占用時間為O(V),此不包括調用DFS-VISIT的時間。
??? 對于每個頂點v(-V,過程DFS-VISIT僅被調用依次,因為只有對白色頂點才會調用此過程。
第4-7行,執行時間為O(E)。
因此,總的執行時間為O(V+E)。
?
下面的鏈接,給出了深度優先搜索的演示系統:

圖的深度優先遍歷演示系統:

http://sjjg.js.zwu.edu.cn/SFXX/sf1/sdyxbl.html

?

===============

最后,咱們再來看深度優先搜索的遞歸實現與非遞歸實現
1、DFS 遞歸實現:

void dftR(PGraphMatrix inGraph) {PVexType v; assertF(inGraph!=NULL,"in dftR, pass in inGraph is null/n");printf("/n===start of dft recursive version===/n");for(v=firstVertex(inGraph);v!=NULL;v=nextVertex(inGraph,v))if(v->marked==0)dfsR(inGraph,v);printf("/n===end of dft recursive version===/n"); } void dfsR(PGraphMatrix inGraph,PVexType inV) {PVexType v1;assertF(inGraph!=NULL,"in dfsR,inGraph is null/n");assertF(inV!=NULL,"in dfsR,inV is null/n");inV->marked=1;visit(inV);for(v1=firstAdjacent(inGraph,inV);v1!=NULL;v1=nextAdjacent(inGraph,inV,v1))//v1當為v的鄰接點。 if(v1->marked==0)dfsR(inGraph,v1); }

2、DFS 非遞歸實現
非遞歸版本---借助結點類型為隊列的棧實現
?? 聯系樹的前序遍歷的非遞歸實現:
?? 可知,其中無非是分成“探左”和“訪右”兩大塊訪右需借助棧中彈出的結點進行.
?? 在圖的深度優先搜索中,同樣可分成“深度探索”和“回訪上層未訪結點”兩塊:
??? 1、圖的深度探索這樣一個過程和樹的“探左”完全一致,
只要對已訪問過的結點作一個判定即可。
??? 2、而圖的回訪上層未訪結點和樹的前序遍歷中的“訪右”也是一致的.
但是,對于樹而言,是提供rightSibling這樣的操作的,因而訪右相當好實現。

在這里,若要實現相應的功能,考慮將每一個當前結點的下層結點中,如果有m個未訪問結點,
則最左的一個需要訪問,而將剩余的m-1個結點按從左到右的順序推入一個隊列中。
并將這個隊列壓入一個堆棧中。

?? 這樣,當當前的結點的鄰接點均已訪問或無鄰接點需要回訪時,
則從棧頂的隊列結點中彈出隊列元素,將隊列中的結點元素依次出隊,
若已訪問,則繼續出隊(當當前隊列結點已空時,則繼續出棧,彈出下一個棧頂的隊列),
直至遇到有未訪問結點(訪問并置當前點為該點)或直到棧為空(則當前的深度優先搜索樹停止搜索)。

?

將算法通過精簡過的C源程序的方式描述如下:

//dfsUR:功能從一個樹的某個結點inV發,以深度優先的原則訪問所有與它相鄰的結點 void dfsUR(PGraphMatrix inGraph,PVexType inV) {PSingleRearSeqQueue tmpQ; //定義臨時隊列,用以接受棧頂隊列及壓棧時使用 PSeqStack testStack; //存放當前層中的m-1個未訪問結點構成隊列的堆棧.//一些變量聲明,初始化動作//訪問當前結點 inV->marked=1; //當marked值為1時將不必再訪問。 visit(inV);do{flag2=0;//flag2是一個重要的標志變量,用以、說明當前結點的所有未訪問結點的個數,兩個以上的用2代表//flag2:0:current node has no adjacent which has not been visited.//1:current node has only one adjacent node which has not been visited.//2:current node has more than one adjacent node which have not been visited. v1=firstAdjacent(inGraph,inV); //鄰接點v1 while(v1!=NULL) //訪問當前結點的所有鄰接點 {if(v1->marked==0) //.. { if(flag2==0) //當前結點的鄰接點有0個未訪問 {//首先,訪問最左結點 visit(v1);v1->marked=1; //訪問完成 flag2=1; ////記錄最左兒子 lChildV=v1; //save the current node's first unvisited(has been visited at this time)adjacent node } else if(flag2==1) //當前結點的鄰接點有1個未訪問 {//新建一個隊列,申請空間,并加入第一個結點 flag2=2;}else if(flag2==2)//當前結點的鄰接點有2個未被訪問 {enQueue(tmpQ,v1);}}v1=nextAdjacent(inGraph,inV,v1);}if(flag2==2)//push adjacent nodes which are not visited. { //將存有當前結點的m-1個未訪問鄰接點的隊列壓棧 seqPush(testStack,tmpQ);inV=lChildV;}else if(flag2==1)//only has one adjacent which has been visited. { //只有一個最左兒子,則置當前點為最左兒子 inV=lChildV;}else if(flag2==0)//has no adjacent nodes or all adjacent nodes has been visited { //當當前的結點的鄰接點均已訪問或無鄰接點需要回訪時,則從棧頂的隊列結點中彈出隊列元素,//將隊列中的結點元素依次出隊,若已訪問,則繼續出隊(當當前隊列結點已空時,//則繼續出棧,彈出下一個棧頂的隊列),直至遇到有未訪問結點(訪問并置當前點為該點)或直到棧為空。 flag=0;while(!isNullSeqStack(testStack)&&!flag){ v1=frontQueueInSt(testStack); //返回棧頂結點的隊列中的隊首元素 deQueueInSt(testStack); //將棧頂結點的隊列中的隊首元素彈出 if(v1->marked==0){ visit(v1);v1->marked=1;inV=v1;flag=1; }}} }while(!isNullSeqStack(testStack));//the algorithm ends when the stack is null }

-----------------------------

上述程序的幾點說明:

所以,這里應使用的數據結構的構成方式應該采用下面這種形式:
1)隊列的實現中,每個隊列結點均為圖中的結點指針類型.
定義一個以隊列尾部下標加隊列長度的環形隊列如下:

struct SingleRearSeqQueue; typedef PVexType QElemType; typedef struct SingleRearSeqQueue* PSingleRearSeqQueue; struct SingleRearSeqQueue {int rear;int quelen;QElemType dataPool[MAXNUM]; };

其余基本操作不再贅述.????


2)堆棧的實現中,每個堆棧中的結點元素均為一個指向隊列的指針,定義如下:

#define SEQ_STACK_LEN 1000 #define StackElemType PSingleRearSeqQueue struct SeqStack; typedef struct SeqStack* PSeqStack; struct SeqStack {StackElemType dataArea[SEQ_STACK_LEN];int slot; };

為了提供更好的封裝性,對這個堆棧實現兩種特殊的操作

2.1) deQueueInSt操作用于將棧頂結點的隊列中的隊首元素彈出.

void deQueueInSt(PSeqStack inStack) {if(isEmptyQueue(seqTop(inStack))||isNullSeqStack(inStack)){printf("in deQueueInSt,under flow!/n");return; } deQueue(seqTop(inStack));if(isEmptyQueue(seqTop(inStack)))inStack->slot--; }

2.2) frontQueueInSt操作用以返回棧頂結點的隊列中的隊首元素.

QElemType frontQueueInSt(PSeqStack inStack) {if(isEmptyQueue(seqTop(inStack))||isNullSeqStack(inStack)){printf("in frontQueueInSt,under flow!/n");return '/r'; } return getHeadData(seqTop(inStack)); }

?

===================

ok,本文完。

總結

以上是生活随笔為你收集整理的BFS和DFS优先搜索算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。