1. 深度優先遍歷(Depth-First Traversal)
2.1 圖的深度優先遍歷的遞歸定義
假設給定圖G的初態是所有頂點均未曾訪問過。在G中任選一頂點v爲初始出發點(源點),則深度優先遍歷可定義如下:首先訪問出發點v,並將其標記爲已訪問過;然後依次從v出發搜索v的每個鄰接點w。若w未曾訪問過,則以w爲新的出發點繼續進行深度優先遍歷,直至圖中所有和源點v有路徑相通的頂點(亦稱爲從源點可達的頂點)均已被訪問爲止。若此時圖中仍有未訪問的頂點,則另選一個尚未訪問的頂點作爲新的源點重複上述過程,直至圖中所有頂點均已被訪問爲止。
圖的深度優先遍歷類似於樹的前序遍歷。採用的搜索方法的特點是儘可能先對縱深方向進行搜索。這種搜索方法稱爲深度優先搜索(Depth-First Search)。相應地,用此方法遍歷圖就很自然地稱之爲圖的深度優先遍歷。
1.2 深度優先搜索的過程
設x是當前被訪問頂點,在對x做過訪問標記後,選擇一條從x出發的未檢測過的邊(x,y)。若發現頂點y已訪問過,則重新選擇另一條從x出發的未檢測過的邊,否則沿邊(x,y)到達未曾訪問過的y,對y訪問並將其標記爲已訪問過;然後從y開始搜索,直到搜索完從y出發的所有路徑,即訪問完所有從y出發可達的頂點之後,才回溯到頂點x,並且再選擇一條從x出發的未檢測過的邊。上述過程直至從x出發的所有邊都已檢測過爲止。此時,若x不是源點,則回溯到在x之前被訪問過的頂點;否則圖中所有和源點有路徑相通的頂點(即從源點可達的所有頂點)都已被訪問過,若圖G是連通圖,則遍歷過程結束,否則繼續選擇一個尚未被訪問的頂點作爲新源點,進行新的搜索過程。
2. 圖路徑搜索實際問題
求下圖中節點0到節點5的所有路徑:
2.1 求解過程
第一步:建立相鄰節點存儲表,節點0與節點1和2;節點1與節點2、3和4;節點2與節點4;節點3與節點4和5;節點4與節點5;
第二步建立空棧,將其實節點0壓人棧中;
1、 我們建立一個存儲結點的棧結構,將起點0入棧,將結點0標記爲入棧狀態;
2、 從結點0出發,找到結點3的第一個非入棧狀態的鄰結點2,將結點2標記爲入棧狀態;
3、 從結點2出發,找到結點2的第一個非入棧狀態的鄰結點4,將結點4標記爲入棧狀態;
4、 從結點4出發,找到結點4的第一個非入棧狀態的鄰結點5,將結點5標記爲入棧狀態;
5、 棧頂結點5是終點,那麼,我們就找到了一條起點到終點的路徑,輸出這條路徑;
6、 從棧頂彈出結點5,將5標記爲非入棧狀態;
7、 現在棧頂結點爲5,結點5沒有除終點外的非入棧狀態的結點,所以從棧頂將結點5彈出;
8、現在棧頂結點爲4,結點4除了剛出棧的結點5之外,沒有非入棧狀態的相鄰結點,那麼我們將結點4出棧;
9、現在棧頂爲結點2,結點2除了剛出棧的結點4之外,沒有非入棧狀態的相鄰結點,那麼我們將結點2出棧;
10、現在棧頂結點爲0,從節點0出發,找到結點0的第一個非入棧狀態的鄰結點1,將結點1標記爲入棧狀態;一直查找到終點節點5。
11、重複步驟7-11,就可以找到從起點3到終點6的所有路徑;
12、棧爲空,算法結束。
求解詳細代碼如下:
#include <iostream>
#include <map>
using namespace std;
class node
{
public:
int number;
node *next;
node(int a,node *b)
{
number=a;
next=b;
}
};
class stacks
{
public:
node * top;
stacks(node * a=NULL)
{
top=NULL;
}
void push(int a)
{
if (top==NULL)
top =new node(a,NULL);
else top=new node(a,top);
}
void pop()
{
node *b=top;
top=top->next;
delete b;
}
}; //保存已加入路徑結點的棧
node* neighbour(int a);// ,int b
int cur_node;//當前結點,即爲棧頂的結點
int next_node=8 ;//當前結點的下一個鄰接點,即剛從棧頂彈出的結點,初始化爲8
map<int,int> map_next;//每個結點的下一個鄰接點,即剛從棧頂彈出的結點
int start=0;
int end=5;//起點爲3,終點爲6
stacks aray[6]={stacks(NULL),stacks(NULL),stacks(NULL),stacks(NULL),stacks(NULL),stacks(NULL)};
stacks stack(NULL);
int states[6];//保存結點狀態的數組
int main()
{
//初始化map_next
for(int i=0;i<=5;i++)
{
map_next[i] = -1;
}
//初始化節點鄰接表的信息
aray[0].push(1);aray[0].push(2);
aray[1].push(2);aray[1].push(3);aray[1].push(4);
aray[2].push(4);
aray[3].push(4); aray[3].push(5);
aray[4].push(5);
stack.push(start);//將起點入棧
states[start]=1;//將起點標記爲入棧狀態
node* tempnode=neighbour(3);
cout<<"鄰居節點:"<<tempnode->number<<"下一個節點:" <<tempnode->next <<endl;
int path[6];int len=0;
while(NULL != stack.top) {//棧不爲空
if (stack.top->number==end){
cout<<"end->start: ";
node* abc=stack.top;
int i;
i=0;
while(abc->number != start){
cout<<abc->number<<",";
path[i++]=abc->number;
abc=abc->next;
}
len=i+1;
cout<<start <<" "<<"路徑節點數="<<len<<endl;
//cout << "start"<<endl;//輸出已找到的路徑
stack.pop();//將棧頂結點彈出
states[end]=0;//清除終點的狀態
map_next[end]=-1;
}
else{
cur_node=stack.top->number;
if(neighbour(cur_node) != NULL){//鄰居不爲空
node *d =neighbour(cur_node);
map_next[cur_node] = d->number;
cur_node=d->number;
stack.push(cur_node);
states[cur_node]=1;
}
else{
stack.pop();
states[cur_node]=0;//刪除的棧頂節點states置爲0 通過該節點到不了目的地
map_next[cur_node] = -1;
}
}
}
return 0;
}
node* neighbour(int a){//,int b
node *abc=aray[a].top;
while ((NULL!=abc)){//結點abc不空
if( states[abc->number]==1 )//鄰居節點已經在棧stack裏了
{
abc=abc->next;//所有的鄰居節點初始化成一個棧了,繼續找下一個鄰居節點
}
else//不在棧stack裏
{
if(-1 == map_next[a])//就要abc作爲返回值 如果a是最棧頂節點
{
while(NULL!=abc && states[abc->number]==1)
{
abc = abc->next;
}
return abc;
}
else if(abc->number == map_next[a]) //判斷當前節點是否是a節點的鄰居節點
{
abc=abc->next;
while(NULL!=abc && states[abc->number]==1)
{
abc = abc->next;
}
return abc; //將abc的下一個結點返回
}
else
{
abc=abc->next;
}
}
}
return NULL;
}
程序運行結果如下;