深度優先和廣度優先演算法的尋路問題求解
兩個演算法尋路的基本思路都是首先判斷從起點是否能到達目標點,再一步一步從目標點返回到起點,得到路徑。
比如要從下面圖的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;
}