1. 程式人生 > >三、【圖演算法】深度優先搜尋(DFS)

三、【圖演算法】深度優先搜尋(DFS)

圖演算法是個龐大的家族,其中大部分成員的主體框架都可以歸結於圖的遍歷。圖的遍歷需要訪問所有頂點一次且僅 一次,此外,圖遍歷同時還要訪問所有的邊一次且僅一次。經過一次遍歷,樹邊的頂點共同構成了原圖的一顆遍歷樹

為防止對頂點的重複訪問,在遍歷的過程中,需要動態地設定各頂點的不同狀態,並且隨著遍歷的程序不斷地轉換狀態,直至最後的“訪問完畢”,圖的遍歷更加強調對處於特定狀態頂頂啊的甄別和查詢,故也稱作圖搜尋

最基本而典型的圖搜尋演算法包括:廣度優先搜尋(BFS),深度優先搜尋(DFS),優先順序搜尋等(PFS),本文主要介紹圖的深度優先搜尋(depth-first search,DFS),本文使用的圖資料結構參見之前部落格

https://blog.csdn.net/qq_18108083/article/details/84870399

策略:優先選取最後一個被訪問到的頂點的鄰居。始自圖中頂點s的BFS搜尋,將首先訪問頂點s,再從s所有尚未訪問到的鄰居中任取一個頂點,並以此為基點,遞迴地執行DFS搜尋。這個過程具有先入先後的特點,故採用棧結構作為輔助容器或直接使用遞迴方法,這裡使用遞迴進行實現。

實現:由於整個圖可能具有多個連通域,從單個頂點開始的DFS可能不能遍歷到圖中的所有頂點,所以DFS函式能夠遍歷從頂點s開始的單個連通域,而dfs函式則對所有頂點進行檢查,只要未曾被訪問過,就從該點開始一次新的DFS搜尋,這樣就能保證所有的連通域都能夠被遍歷到。

template<typename Tv, typename Te> void graph<Tv, Te>::DFS(int v, int& clock)
{
	status(v) = DISCOVERED;    //標記當前節點為發現
	dTime(v) = ++clock;

	for (int u = firstNbr(v); u > -1; u = nextNbr(v, u))  //遍歷所有鄰居頂點
	{
		switch (status(u))
		{
		case UNDISCOVERED:   //尚未發現的頂點,繼續深入遍歷
			status(u) = DISCOVERED;  //標記為已發現
			type(v, u) = TREE;
			parent(u) = v;
			DFS(u, clock);
			break;
		case DISCOVERED:     //已被發現但是尚未遍歷完成的頂點,那就是祖先啊
			type(v, u) = BACKWARD;
			break;
		default:   //VISITED  已經遍歷完成,根據dTime判斷是FORWARD還是CROSS
			type(v, u) = (dTime(v) < dTime(u)) ? FORWARD : CROSS;
			break;
		}
	}
	status(v) = VISITED;
	fTime(v) = ++clock;
}

template<typename Tv, typename Te> void graph<Tv, Te>::dfs(int s)
{
	reset();   //復位所有頂點和已存在邊的狀態為未被發現,未確定
	int clock = 0;  //時間標籤
	int v = s;
	do
	{
		if (status(v) == UNDISCOVERED)
			DFS(v, clock);   //對每個頂點都進行一次單連通域深度優先搜尋
		cout << "v----" << v << endl;
	} while ((v=(++v%n)) != s);
}

效率:若圖G=(V,E)中共有n個頂點和e條邊,則DFS僅需O(n+e)時間。