1. 程式人生 > >圖的幾種遍歷方式

圖的幾種遍歷方式

1.圖的遍歷方式大概也就兩種吧,DFS和BFS。但是有不同的實現方式,上次聽說我在遞迴轉非遞迴演算法中實現了二叉樹的遞迴轉非遞迴,然後有人說那圖的呢?其實圖的DFS轉成非遞迴還要簡單一些。

2.首先是BFS,BFS其實很簡單了。使用佇列來儲存與當前節點相鄰的所有節點,然後用這些節點作為基礎繼續拓展。我們這裡首先給出圖的定義和初始化的程式碼。

#define MAX 10
typedef struct graph
{
	int n;   //頂點數
	int e;  //邊數
	int edge[MAX][MAX];  
}Graph;
int visit[MAX];
void InitGraph(Graph &G)
{
	for (int i = 0; i < MAX; i++)
		for (int j = 0; j < MAX; j++)
			G.edge[i][j] = 0;
}

這是一個有向圖,如果有連結的邊我們把這個邊初始化為非零的數字。一般使用這個邊的邊權來初始化這個資料。但是這裡我們假定如果有連邊存在那麼這個邊緣陣列被初始化為1,那麼BFS的寫法就是這樣的。

//廣度優先搜尋演算法
void BFS(Graph G, int num)  //num為從該點開始進行搜尋
{
	queue<int>Queue;
	cout << num << " ";
	visit[num] = 1;
	Queue.push(num);  //訪問完該點後入佇列
	while (!Queue.empty())  //佇列不為空時
	{
		num = Queue.front();  //出隊
		Queue.pop();
		for (int i = 0; i < G.n; i++)
		{
			if (G.edge[num][i] != 0 && visit[i] == 0)   //找到與之相連的頂點入隊
			{
				cout << i << " ";
				Queue.push(i);
				visit[i] = 1;
			}
		}
	}
	cout << endl;
}

有了BFS那麼下一個就是DFS了。深度優先搜尋我們一般都是用遞迴寫的,但是肯定也是可以採用非遞迴實現的。我們這裡還是先實現一下遞迴。我們知道DFS的思想就是找到一條路之後繼續向下尋找,而不是擴大尋找的點。這樣寫成程式碼大概就是這樣的

void DFS(Graph G, int num) //深度優先搜尋遞迴演算法,G還是圖,num還是搜尋的起點
{
	int i;
	cout << num << " ";//輸出
	visit[num] = 1;//標記這裡已經訪問過
	for (i = 0; i < G.n; i++)
	{
		if (G.edge[num][i] != 0 && visit[i] == 0)
			DFS(G, i);//找到鄰接的點向下訪問
	}
}

遞迴寫的很簡單,但是我們現在把它轉化為非遞迴的實現。遞迴轉非遞迴肯定是要用到棧了。我們來模擬這個遞迴的過程,訪問到某一個節點之後,我們尋找他的下一個節點,並且把這個節點壓進棧中然後彈出來依次訪問這個棧中的元素。即可達到效果。為什麼會是這個樣子呢?我們來簡單的解釋一下,我懶得畫圖了,解跟著我想象吧。假設我們1號節點連結了2和3那麼我們從一號開始,訪問1之後2和3就會被壓進棧中,有人可能有疑惑?那這不就是BFS嗎?因為所有與它相鄰的節點又被拿出來了。沒錯,的確是被拿出來了,但是我們沒有訪問啊。假設現在棧中已經有了2和3中,我們在進行第二步迴圈的時候就會以2位起點來找到和2相鄰的點,這樣找下去其實這個搜尋的確實是在深入的。因為雖然我們把所有相鄰的點都拿出來放進棧中了,但是我們沒有一次 他她們全部訪問而是一個一個的訪問。利用了棧的特性實現了DFS的非遞迴。其實也很簡單的吧下面是實現的程式碼:

void dfs(Graph G, int Num)
{
	stack<int>st;
	visit[Num] = 1;
	st.push(Num);
	while (!st.empty())
	{
		Num = st.top();
		st.pop();
		cout << Num <<" ";
		for (int i = G.n - 1; i >= 0; i--)
		{
			if (G.edge[Num][i] != 0 && visit[i] == 0)
			{
				st.push(i);
				visit[i] = 1;
			}
		}
	}
}