❶ 基本演算法——深度優先搜索(DFS)和廣度優先搜索(BFS)
深度優先搜索和廣度優先搜索,都是圖形搜索演算法,它兩相似,又卻不同,在應用上也被用到不同的地方。這里拿一起討論,方便比較。
一、深度優先搜索
深度優先搜索屬於圖演算法的一種,是一個針對圖和樹的遍歷演算法,英文縮寫為DFS即Depth First Search。深度優先搜索是圖論中的經典演算法,利用深度優先搜索演算法可以產生目標圖的相應拓撲排序表,利用拓撲排序表可以方便的解決很多相關的圖論問題,如最大路徑問題等等。一般用堆數據結構來輔助實現DFS演算法。其過程簡要來說是對每一個可能的分支路徑深入到不能再深入為止,而且每個節點只能訪問一次。
基本步奏
(1)對於下面的樹而言,DFS方法首先從根節點1開始,其搜索節點順序是1,2,3,4,5,6,7,8(假定左分枝和右分枝中優先選擇左分枝)。
(2)從stack中訪問棧頂的點;
(3)找出與此點鄰接的且尚未遍歷的點,進行標記,然後放入stack中,依次進行;
(4)如果此點沒有尚未遍歷的鄰接點,則將此點從stack中彈出,再按照(3)依次進行;
(5)直到遍歷完整個樹,stack里的元素都將彈出,最後棧為空,DFS遍歷完成。
二、廣度優先搜索
廣度優先搜索(也稱寬度優先搜索,縮寫BFS,以下採用廣度來描述)是連通圖的一種遍歷演算法這一演算法也是很多重要的圖的演算法的原型。Dijkstra單源最短路徑演算法和Prim最小生成樹演算法都採用了和寬度優先搜索類似的思想。其別名又叫BFS,屬於一種盲目搜尋法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。換句話說,它並不考慮結果的可能位置,徹底地搜索整張圖,直到找到結果為止。基本過程,BFS是從根節點開始,沿著樹(圖)的寬度遍歷樹(圖)的節點。如果所有節點均被訪問,則演算法中止。一般用隊列數據結構來輔助實現BFS演算法。
基本步奏
(1)給出一連通圖,如圖,初始化全是白色(未訪問);
(2)搜索起點V1(灰色);
(3)已搜索V1(黑色),即將搜索V2,V3,V4(標灰);
(4)對V2,V3,V4重復以上操作;
(5)直到終點V7被染灰,終止;
(6)最短路徑為V1,V4,V7.
❷ 數據結構C語言版 圖的遍歷 DFS和BFS演算法,用鄰接矩陣儲存 急阿在線等 求大神指點
#include <iostream>
#include<string.h>
#include<stack>
#include<queue>
const int Max=100;
const int VISITED=101010;
const int UNVISITED=111111;
const int AFFINITY=101010;
const int INFINITY=111111;
using namespace std;
class Edge
{
public:
int start;
int end;
int weight;
Edge(int st=0,int en=0,int w=0):start(st),end(en),weight(w){}
bool operator>(Edge oneEdge){return weight>oneEdge.weight?true:false;}
bool operator<(Edge oneEdge){return weight<oneEdge.weight?true:false;}
bool operator!=(Edge oneEdge)
{
if(weight!=oneEdge.weight||start!=oneEdge.start||end!=oneEdge.end)
return true;
return false;
}
};
class AdjGraf
{
private:
int verticesNum;
int edgeNum;
int **matrix;
int *Mark;
public:
AdjGraf(int vert)
{
int i=0,j=0;
verticesNum=vert;
matrix=(int**)new int*[vert];
for(i=0;i<vert;i++)
matrix[i]=new int[vert];
Mark=new int[vert];
for(i=0;i<vert;i++)
for(j=0;j<vert;j++)
{
matrix[i][j]=0;
}
for( int m=0;m<verticesNum;m++)
Mark[m]=UNVISITED;
}
~AdjGraf();
//返回與頂點oneVertex相關聯的第一條邊
Edge FirstEdge(int oneVertex);
//返回與邊PreEdge有相同關聯頂點oneVertex的下一條邊
Edge NextEdge( Edge preEdge);
//添加一條邊
void setEdge(int fromVertex,int toVertex,int weight);
//刪一條邊
void delEdge(int fromVertex,int toVertex);
//如果oneEdge是邊則返回TRUE,否則返回FALSE
bool IsEdge( Edge oneEdge)
{
if(oneEdge.start>=0&&oneEdge.start<verticesNum&&
oneEdge.end>=0&&oneEdge.end<verticesNum)
return true;
else return false;
}
//返回邊oneEdge的始點
int FromVertex(Edge oneEdge){return oneEdge.start;}
//返回邊oneEdge的終點
int ToVertex(Edge oneEdge){return oneEdge.end;}
//返回邊oneEdge的權
int Weight(Edge oneEdge){return oneEdge.weight;}
void visit(int i){cout<<i+1<<" ";}
void BFS(int i=1);
void DFS(int i);
void DFSTraverse(int v);
void DFSNoReverse(int f=1);
Edge UNVISITEDEdge(int f);
};
AdjGraf::~AdjGraf()
{
for(int i=0;i<verticesNum;i++)
delete[]matrix[i];
delete[]matrix;
}
Edge AdjGraf::FirstEdge(int oneVertex)
{ int i;
Edge tempEdge;
tempEdge.start=oneVertex;
for( i=0;i<verticesNum;i++)
if(matrix[oneVertex][i]!=0)
break;
tempEdge.end=i;
tempEdge.weight=matrix[oneVertex][i];
return tempEdge;
}
Edge AdjGraf ::NextEdge( Edge preEdge)
{
Edge tempEdge;
tempEdge.start=preEdge.start;
int i=0;
for(i=preEdge.end+1;i<verticesNum;i++)
if(matrix[preEdge.start][i]!=0)
break;
tempEdge.end=i;
tempEdge.weight=matrix[preEdge.start][i];
return tempEdge;
}
void AdjGraf::setEdge(int fromVertex,int toVertex,int weight)
{
if(matrix[fromVertex-1][toVertex-1]==0)
edgeNum++;
matrix[fromVertex-1][toVertex-1]=weight;
}
void AdjGraf::delEdge(int fromVertex,int toVertex)
{
if(matrix[fromVertex][toVertex]==0)
edgeNum--;
matrix[fromVertex][toVertex]=0;
}
/*************遞歸實現深度優先****************/
void AdjGraf::DFS(int i)
{
visit(i);
Mark[i]=VISITED;
for(Edge e=FirstEdge(i);IsEdge(e);e=NextEdge(e))
if(Mark[ToVertex(e)] == UNVISITED)
DFS(ToVertex(e));
}
void AdjGraf::DFSTraverse(int v)
{
v--;
int i;
for(i=0;i<verticesNum;i++)
Mark[i]=UNVISITED;
for(i=v;i<v+verticesNum;i++)
if (Mark[i]== UNVISITED)
DFS(i);
}
Edge AdjGraf::UNVISITEDEdge(int f)
{ int i;
for( Edge e=FirstEdge(f);IsEdge(e);e=NextEdge(e))
if(Mark[e.end]==UNVISITED)
return e;
return Edge(verticesNum,verticesNum,0) ;
}
/*************非遞歸實現深度優先**************/
void AdjGraf::DFSNoReverse(int f)
{
f--;
int i,counter=0,j,flag;
stack<int>Temp;
for(i=0;i<verticesNum;i++)
Mark[i]=UNVISITED;
flag=f;
while(counter<12)
{
while(flag!=verticesNum&&IsEdge(UNVISITEDEdge(flag))||!Temp.empty())
{
// Edge tempEdge=UNVISITEDEdge(j);
while(flag!=verticesNum&&Mark[flag]==UNVISITED)
{
visit(flag);
Mark[flag]=VISITED;
Temp.push(flag);
flag=UNVISITEDEdge(flag).end;
}
if(!Temp.empty())
{
flag=UNVISITEDEdge(Temp.top()).end;
Temp.pop();
}
}
if(Mark[counter]==UNVISITED) flag=counter;
else counter++;
}
}
/*************非遞歸實現廣度優先**************/
void AdjGraf::BFS(int v)
{
int i;
v--;
for( i=0;i<verticesNum;i++)
Mark[i]=UNVISITED;
queue<int>tempqueue;
i=0;
/*********v先從指定位置開始,然後從v=0,1,2......
依次檢查是否有孤立結點*****************/
while(i<verticesNum)
{
tempqueue.push(v);
while(!tempqueue.empty())
{
v=tempqueue.front();
tempqueue.pop();
if(Mark[v]==UNVISITED)
{
visit(v);
Mark[v]=VISITED;
for(Edge e=FirstEdge(v);IsEdge(e);e=NextEdge(e))
{
v=ToVertex(e);
tempqueue.push(v);
}
}
}
/***********防止出現孤立點****************/
if(Mark[i]==VISITED) i++;
else v=i;
}
}
int main()
{
AdjGraf Graph(12);
Graph.setEdge(1,2,1);
Graph.setEdge(2,1,1);
Graph.setEdge(1,3,5);
Graph.setEdge(3,1,5);/** V1 V12 V11 */
Graph.setEdge(2,4,3);/** / \ / \ */
Graph.setEdge(4,2,3);/** v2 v3 V10 V9 */
Graph.setEdge(2,5,7);/** / \ / \ */
Graph.setEdge(5,2,7);/** v4 v5 v6-v7 */
Graph.setEdge(4,8,4);/** \ / */
Graph.setEdge(8,4,4);/** v8 */
Graph.setEdge(5,8,3);
Graph.setEdge(8,5,3);
Graph.setEdge(3,6,2);
Graph.setEdge(6,3,2);
Graph.setEdge(3,7,1);
Graph.setEdge(7,3,1);
Graph.setEdge(6,7,6);
Graph.setEdge(7,6,6);
Graph.setEdge(12,9,6);
Graph.setEdge(9,12,6);
Graph.setEdge(12,10,6);
Graph.setEdge(10,12,6);
Graph.setEdge(11,11,6);
cout<<"DFSTraverse:"<<endl;
Graph.DFSTraverse(3);
cout<<endl;
cout<<"DFSNoReverse:"<<endl;
Graph.DFSNoReverse(3);
cout<<endl;
cout<<"BFS:"<<endl;
Graph.BFS(3);
cout<<endl;
return 0;
}
以上代碼運行環境codeblocks 程序採用DFS遞歸演算法 DFS非遞歸演算法 BFS非遞歸演算法
望採納~
❸ (轉)BFS與DFS
廣度優先搜索(BFS)和深度優先搜索(DFS)都是圖遍歷演算法中的重要成員。BFS採用的策略是:越早被訪問到的頂點,其鄰居越優先被訪問。類似於樹的層次遍歷。DFS採用的策略是:優先選取最後一個被訪問到的頂點的鄰居。類似於樹的前序遍歷。
BFS演算法的時間比較簡單。
頂點的狀態有三種:(1)visited;(2)discovered;(3)undiscovered;
採用輔助隊列Q存放已經遍歷的頂點。每一次迭代都從Q中取出當前的首頂點v;在逐一核對其鄰居狀態u的狀態並做相應的處理,最後將頂點v置為visited,即可進入下一步迭代。
DFS演算法是優先選取最後一個被訪問到的頂點的鄰居。因此可以棧作為輔助的數據結構(當然不用也行,只是為了好實現)。DFS的每一次迭代中,都先將當前節點v標記為discovered狀態,再逐一核對其各鄰居u的狀態並做相應的處理,待其所有鄰居均已處理完畢之後,將頂點v設置為visited狀態,然後進行回溯。
DFS演算法重要的有一個活躍期的時間區間。通過活躍期可以判斷兩個頂點之間是否是祖先-後代關系,判斷的充要條件是兩個頂點的活躍期是否相互包含。
BFS和DFS的時間復雜度都是O(n+e),其中n是頂點數,e是邊的數量。
參考:
https://www.cnblogs.com/brucekun/p/8503042.html
❹ 用C語言編寫三個演算法,BFS或DFS,爬山演算法,遺傳演算法實現八皇後問題
網路演算法名,加上八皇後
比如
BFS 八皇後問題 C語言。
或者
遺傳演算法 八皇後問題 C語言
然後根據搜索結果 就可以得到演算法和代碼了。
❺ 小弟是ACM弱渣,一直不知道DFS和bfs在哪種場合用更加好
DFS 和 BFS使用哪種跟具體的數據(源點和目標的位置)有關,比如你數據結構是樹,要搜索根節點到某一葉子節點的路徑。這時候,如果這個葉子節點距離根節點比較遠,使用深度優先DFS效率最高。如果這個葉子節點距離根節點比較近,使用廣度優先BFS效率更高。你可以自己畫一個樹,自己用腦子模擬一下這兩個演算法,一下子就明白了。
❻ dfs生成森林和bfs生成森林怎麼畫
對強連通有向圖,用DFS和BFS演算法可分別求得DFS和BFS生成樹,對非強連通圖,則一般只能得到生成森林。
❼ 求解關於DFS,BFS的演算法時間復雜度分析
記住就行了,DFS、BFS時間復雜度對於採用臨接矩陣存儲時是O(n);對於採用臨接表時是O(n+e).
❽ 圖的dfs,bfs是否唯一,導致不唯一的因素有哪些
DFS 和 BFS 都是唯一的,
DFS = 貪婪演算法 = 一條道走到結尾(有可能這不是最短的).
BFS = 常用路徑演算法 = 就是你也三條路, 你第一條路走一步,第二條也走一步,第三條路也走一步.
BFS 肯定是唯一的, 因為是平均走的, dfs有可能不是唯一的假如你選擇的順序不一樣,比如
路徑 A->B or D->C 你想先從D走那就是ADC 然後 ABC 反之亦然.
所以BFS是穩定的,唯一的,也是最常用的路徑演算法。
❾ JS 深度優先遍歷(DFS)和廣度優先遍歷(BFS)
深度優先遍歷DFS
自定義:深度單線遊走,從根走完最後一個節點,在遊走兄弟節點,走完兄弟的所有子節點,循環之。
遞歸演算法:
function deepFirstSearch(node, nodeList = []) {
if (node) {
nodeList.push(node);
var children = node.children;
for (var i = 0; i < children.length; i++)
//每次遞歸的時候將 需要遍歷的節點 和 節點所存儲的數組傳下去
deepFirstSearch(children[i], nodeList);
}
return nodeList;
}
非遞歸演算法:
function deepFirstSearch(node) {
var nodes = [];
if (node != null) {
var stack = [];
stack.push(node);
while (stack.length != 0) {
var item = stack.pop();
nodes.push(item);
var children = item.children;
for (var i = children.length - 1; i >= 0; i--)
stack.push(children[i]);
}
}
return nodes;
}
廣度優先遍歷(BFS)
自定義:從根開始 層層推進 走完一層 走下一層 (猶如爬樓,走完一層的樓梯,繼續下一層的樓梯)
遞歸演算法:(容易棧溢出)
function breadthFirstSearch(node) {
var nodes = [];
var i = 0;
if (!(node == null)) {
nodes.push(node);
breadthFirstSearch(node.nextElementSibling);
node = nodes[i++];
breadthFirstSearch(node.firstElementChild);
}
return nodes;
}
非遞歸演算法:(推薦)
function breadthFirstSearch(node) {
var nodes = [];
if (node != null) {
var queue = [];
queue.unshift(node);
while (queue.length != 0) {
var item = queue.shift();
nodes.push(item);
var children = item.children;
for (var i = 0; i < children.length; i++)
queue.push(children[i]);
}
}
return nodes;
}
❿ 網路流的最大流演算法
1、augment path,直譯為「增廣路徑」,其思想大致如下:
原有網路為G,設有一輔助圖G',其定義為V(G') = V(G),E(G')初始值(也就是容量)與E(G)相同。每次操作時從Source點搜索出一條到Sink點的路徑,然後將該路徑上所有的容量減去該路徑上容量的最小值,然後對路徑上每一條邊<u,v>添加或擴大反方向的容量,大小就是剛才減去的容量。一直到沒有路為止。此時輔助圖上的正向流就是最大流。
我們很容易覺得這個演算法會陷入死循環,但事實上不是這樣的。我們只需要注意到每次網路中由Source到Sink的流都增加了,若容量都是整數,則這個演算法必然會結束。
尋找通路的時候可以用DFS,BFS最短路等演算法。就這兩者來說,BFS要比DFS快得多,但是編碼量也會相應上一個數量級。
增廣路方法可以解決最大流問題,然而它有一個不可避免的缺陷,就是在極端情況下每次只能將流擴大1(假設容量、流為整數),這樣會造成性能上的很大問題,解決這個問題有一個復雜得多的演算法,就是預推進演算法。
2、push label,直譯為「預推進」演算法。
3、壓入與重標記(Push-Relabel)演算法
除了用各種方法在剩餘網路中不斷找增廣路(augmenting)的Ford-Fulkerson系的演算法外,還有一種求最大流的演算法被稱為壓入與重標記(Push-Relabel)演算法。它的基本操作有:壓入,作用於一條邊,將邊的始點的預流盡可能多的壓向終點;重標記,作用於一個點,將它的高度(也就是label)設為所有鄰接點的高度的最小值加一。Push-Relabel系的演算法普遍要比Ford-Fulkerson系的演算法快,但是缺點是相對難以理解。
Relabel-to-Front使用一個鏈表保存溢出頂點,用Discharge操作不斷使溢出頂點不再溢出。Discharge的操作過程是:若找不到可被壓入的臨邊,則重標記,否則對臨邊壓入,直至點不再溢出。演算法的主過程是:首先將源點出發的所有邊充滿,然後將除源和匯外的所有頂點保存在一個鏈表裡,從鏈表頭開始進行Discharge,如果完成後頂點的高度有所增加,則將這個頂點置於鏈表的頭部,對下一個頂點開始Discharge。
Relabel-to-Front演算法的時間復雜度是O(V^3),還有一個叫Highest Label Preflow Push的演算法復雜度據說是O(V^2*E^0.5)。我研究了一下HLPP,感覺它和Relabel-to-Front本質上沒有區別,因為Relabel-to-Front每次前移的都是高度最高的頂點,所以也相當於每次選擇最高的標號進行更新。還有一個感覺也會很好實現的演算法是使用隊列維護溢出頂點,每次對pop出來的頂點discharge,出現了新的溢出頂點時入隊。
Push-Relabel類的演算法有一個名為gap heuristic的優化,就是當存在一個整數0<k<V,沒有任何頂點滿足h[v]=k時,對所有h[v]>k的頂點v做更新,若它小於V+1就置為V+1。
cpp程序: #include<cstdio>#include<cstring>#include<algorithm>#include<queue>#;inttt,kase;intnn,m;intH[45],X[1004],P[1004],flow[1004],tot,cap[1005];intd[45];intS,T;voidadd(intx,inty,intz){P[++tot]=y;X[tot]=H[x];H[x]=tot;flow[tot]=z;cap[tot]=flow[tot];}queue<int>q;boolbfs(){memset(d,0,sizeof(d));d[S]=1;intx;q.push(S);while(!q.empty()){x=q.front();q.pop();for(inti=H[x];i;i=X[i]){if(flow[i]>0&&!d[P[i]]){d[P[i]]=d[x]+1;q.push(P[i]);}}}returnd[T];}intdfs(intx,inta){if(x==T||a==0)returna;intf=a,tmp;for(inti=H[x];i;i=X[i]){if(flow[i]>0&&d[P[i]]==d[x]+1){tmp=dfs(P[i],min(flow[i],a));flow[i]-=tmp;a-=tmp;flow[i^1]+=tmp;if(!a)break;}}if(f==a)d[x]=-1;returnf-a;}intDinic(){intf=0;while(bfs())f+=dfs(S,inf);returnf;}intmain(){/**輸入過程省略**/intmaxflow=Dinic();printf(%d
,maxflow);return0;}