1. 程式人生 > >深度優先和廣度優先演算法的尋路問題求解

深度優先和廣度優先演算法的尋路問題求解

兩個演算法尋路的基本思路都是首先判斷從起點是否能到達目標點,再一步一步從目標點返回到起點,得到路徑。

比如要從下面圖的0節點到7號節點,首先利用兩種演算法判斷0號是否能到7號,再從7號返回到起點0點。

首先簡單介紹以下兩種演算法,這兩種圖的搜尋方法都可以簡單地類比為二叉樹的遍歷演算法。

深度優先搜尋:類似於樹的先序遍歷(根節點->左子樹的根節點->右子樹的根節點),結果為:0(1(378)4)(256)

廣度優先搜尋:類似於樹的層次序遍歷(本層的節點->下一層的節點),結果為:012345678

一、建立無向圖G

typedef struct {
	int vexnum;//頂點的個數
	AdjMatrix arcs;//圖的鄰接矩陣
}Graph;

要判斷0節點是否能通到7節點,則應採用鄰接矩陣,其中的元素用0,1來表示兩個節點是否可以直接到達。比如上面的圖,0節點可以到1,2節點,則V{0,1}=V{0,2}=1.這樣做的目的是讓程式知道哪條路是可行的。

二、兩種演算法都是通過訪問矩陣,尋找與本節點相連的未被訪問過的節點。

因為本問題要解決的是找出路徑,所以在找到下一個節點後,當前節點應該以父節點的名義儲存。然後在找到目標節點後,依次向上尋找父節點,以及它的父節點……

typedef struct {
	int current;
	int parent;
}node;

1. 深度優先搜尋:

由於在資料量大的情況下,遞迴演算法會造成溢位,所以採用棧來代替遞迴。即首先把在同一層的節點從右到左壓入棧中(比如圖中的第二層,首先將2壓入棧中,再將1壓入棧中,這時第二層與第一層的連通節點(1,2)訪問完畢,從棧中彈出一個數,1,再把1的“子樹”的第二層從右到左壓入棧……一直到棧中沒有元素)。這表明很多遞迴可以通過棧來非遞迴化。

vector<int> DFS(Graph G, int start, int target)//深度優先
{
	bool visited[MaxVnum]={false};
	stack<int> stk;
	vector<int> DFSvec;
	node temp;
	vector<node> output;
	visited[start] = true; //從V開始訪問,flag它
	temp.current = start;
	temp.parent = -1;
	output.push_back(temp);
	for (int j = G.vexnum - 1; j >= 0; j--) {
		if (G.arcs[start][j] == 1 && visited[j] == false) //這裡可以獲得V未訪問過的鄰接點
		{
			stk.push(j);
			visited[j] = true;
			temp.parent = start;
			temp.current = j;
			output.push_back(temp);
		}
	}
	while (!stk.empty())
	{
		int temp1 = stk.top();
		//cout << temp1;
		if (temp.current == target) break;
		stk.pop();
		for (int j = G.vexnum - 1; j >= 0; j--) {
			if (G.arcs[temp1][j] == 1 && visited[j] == false) //這裡可以獲得V未訪問過的鄰接點
			{
				stk.push(j);
				visited[j] = true;
				temp.parent = temp1;
				temp.current = j;
				output.push_back(temp);
			}

		}
	}
	//尋路
	if(output.size()==1) AfxMessageBox(_T("地圖資料不正"));
	else{
	stack<int> result;
	int j;
	for (j = 0;j<output.size();j++)
	{
		if (output[j].current == target)
		{
			result.push(output[j].current);break;
		}
	}
	int i = j;
	while (output[i].current != start) {
		int temp = output[i].parent;
		for (i = 0;i<output.size();i++)
		{
			if (output[i].current == temp)
			{
				result.push(output[i].current);break;
			}
		}
	}
	while (!result.empty()) {
		DFSvec.push_back(result.top());
		result.pop();
	}
	}
	return DFSvec;
}

2. 廣度優先搜尋:

與深度優先不同,廣度優先是先把同一層的節點遍歷完,再去看下一層節點。所以選用佇列(同樣以圖的第二層為例,首先從左到右把1壓入佇列,再把2壓入佇列,把1彈出,尋找與1相連線的未訪問過的節點,並將其依次壓入佇列,彈出2……)

vector<int> BFS(Graph G, int start, int target)//廣度優先
{

	bool visited[MaxVnum]={false};
	queue<int>temp;
	vector<int> BFSvec;
	node n;
	vector<node> output;
	visited[start] = true; //從V開始訪問,flag它
	n.current = start;
	n.parent = -1;
	output.push_back(n);
	for (int j = 0;j<G.vexnum;j++)
	{
		if (G.arcs[start][j] == 1 && visited[j] == false) //這裡可以獲得V未訪問過的鄰接點
		{
			visited[j] = true;
			temp.push(j);
			//cout << temp1;
			n.parent = start;
			n.current = j;
			output.push_back(n);
		}
	}
	while (temp.size() != 0)
	{
		int a = temp.front();
		temp.pop();
		for (int j = 0;j<G.vexnum;j++) {
			if (G.arcs[a][j] == 1 && visited[j] == false) //這裡可以獲得V未訪問過的鄰接點
			{
				visited[j] = true;
				temp.push(j);
				n.parent = a;
				n.current = j;
				output.push_back(n);
			}
		}
	}
	//尋路
	if(output.size()==1) AfxMessageBox(_T("地圖資料不正"));
	else {
	stack<int> result;
	int j;
	for (j = 0;j<output.size();j++)
	{
		if (output[j].current == target)
		{
			result.push(output[j].current);break;
		}
	}
	int i = j;
	while (output[i].current != start) {
		int temp = output[i].parent;
		for (i = 0;i<output.size();i++)
		{
			if (output[i].current == temp)
			{
				result.push(output[i].current);break;
			}
		}
	}
	while (!result.empty()) {
		BFSvec.push_back(result.top());
		result.pop();
	}}
	return BFSvec;
}