一、深度優先與廣度優先遍歷
從圖中某一頂點出發系統地訪問圖中所有頂點,使每個頂點恰好被訪問一次,這種運算操作被稱爲圖的遍歷。爲了避免重複訪問某個頂點,可以設一個標誌數組visited[i],未訪問時值爲false,訪問一次後就改爲true。
圖的遍歷分爲深度優先遍歷和廣度優先遍歷兩種方法,兩者的時間效率都是O(n*n)。
1.深度優先遍歷
深度優先遍歷與深搜DFS相似,從一個點A出發,將這個點標爲已訪問visited[i]:=true;,然後再訪問所有與之相連,且未被訪問過的點。當A的所有鄰接點都被訪問過後,再退回到A的上一個點(假設是B),再從B的另一個未被訪問的鄰接點出發,繼續遍歷。
下面給出的深度優先遍歷的參考程序,假設圖以鄰接表存儲
void dfs(int i) //圖用數組模擬鄰接表存儲,訪問點i
{
visited[i] = true; //標記爲已經訪問過
for (int j = 1; j <= num[i]; j++) //遍歷與i相關聯的所有未訪問過的頂點
if (!visited[g[i][j]])
dfs(g[i][j]);
}
主程序如下:
int main()
{
……
memset(visited,false,sizeof(visited));
for (int i = 1; i <= n; i++) //每一個點都作爲起點嘗試訪問,因爲不是從任何
//一點開始都能遍歷整個圖
if (!visited[i])
dfs(i);
……
return 0;
}
2.廣度優先遍歷
廣度優先遍歷並不常用,從編程複雜度的角度考慮,通常採用的是深度優先遍歷。
廣度優先遍歷和廣搜BFS相似,因此使用廣度優先遍歷一張圖並不需要掌握什麼新的知識,在原有的廣度優先搜索的基礎上,做一點小小的修改,就成了廣度優先遍歷算法。
二、一筆畫問題
如果一個圖存在一筆畫,則一筆畫的路徑叫做歐拉路,如果最後又回到起點,那這個路徑叫做歐拉回路。
我們定義奇點是指跟這個點相連的邊數目有奇數個的點。對於能夠一筆畫的圖,我們有以下兩個定理。
定理1:存在歐拉路的條件:圖是連通的,有且只有2個奇點。
定理2:存在歐拉回路的條件:圖是連通的,有0個奇點。
兩個定理的正確性是顯而易見的,既然每條邊都要經過一次,那麼對於歐拉路,除了起點和終點外,每個點如果進入了一次,顯然一定要出去一次,顯然是偶點。對於歐拉回路,每個點進入和出去次數一定都是相等的,顯然沒有奇點。
求歐拉路的算法很簡單,使用深度優先遍歷即可。
根據一筆畫的兩個定理,如果尋找歐拉回路,對任意一個點執行深度優先遍歷;找歐拉路,則對一個奇點執行DFS,時間複雜度爲O(m+n),m爲邊數,n是點數。
#include
#include
using namespace std;
#define maxn 101
int g[maxn][maxn]; //此圖用鄰接矩陣存儲
int du[maxn]; //記錄每個點的度,就是相連的邊的數目
int circuit[maxn]; //用來記錄找到的歐拉路的路徑
int n,e,circuitpos,i,j,x,y,start;
void find_circuit(int i) //這個點深度優先遍歷過程尋找歐拉路
{
int j;
for (j = 1; j <= n; j++)
if (g[i][j] == 1) //從任意一個與它相連的點出發
{
g[j][i] = g[i][j] = 0;
find_circuit(j);
}
circuit[++circuitpos] = i; //記錄下路徑
}
int main()
{
memset(g,0,sizeof(g));
cin >> n >> e;
for (i = 1; i <= e; i++)
{
cin >> x >> y;
g[y][x] = g[x][y] = 1;
du[x]++; //統計每個點的度
du[y]++;
}
start = 1; //如果有奇點,就從奇點開始尋找,這樣找到的就是
for (i = 1; i <= n; i++) //歐拉路。沒有奇點就從任意點開始,
if (du[i]%2 == 1) //這樣找到的就是歐拉回路。(因爲每一個點都是偶點)
start = i;
circuitpos = 0;
find_circuit(start);
for (i = 1; i <= circuitpos; i++)
cout << circuit[i] << ’ ';
cout << endl;
return 0;
}
三、哈密爾頓環
歐拉回路是指不重複地走過所有路徑的迴路,而哈密爾頓環是指不重複地走過所有的點,並且最後還能回到起點的迴路。
使用簡單的深度優先搜索,就能求出一張圖中所有的哈密爾頓環。
#include
#include
using namespace std;
int start,length,x,n;
bool visited[101],v1[101];
int ans[101], num[101];
int g[101][101];
void print()
{ int i;
for (i = 1; i <= length; i++)
cout << ’ ’ << ans[i];
cout << endl;
}
void dfs(int last,int i)
{
visited[i] = true; //標記爲已經訪問過
v1[i] = true; //標記爲已在一張圖中出現過
ans[++length] = i; //記錄下答案
for (int j = 1; j <= num[i]; j++)
{
if (g[i][j]==x&&g[i][j]!=last) //回到起點,構成哈密爾頓環
{
ans[++length] = g[i][j];
print(); //這裏說明找到了一個環,則輸出ans數組。
length–;
break;
}
if (!visited[g[i][j]]) dfs(i,g[i][j]);
}
length–;
visited[i] = false;
}
int main()
{
memset(visited,false,sizeof(visited));
memset(v1,false,sizeof(v1));
for (x = 1; x <= n; x++)
//每一個點都作爲起點嘗試訪問,因爲不是從任何一點開始都能找過整個圖的
if (!v1[x]) //如果點x不在之前曾經被訪問過的圖裏。
{
length = 0; //定義一個ans數組存答案,length記答案的長度。
dfs(x);
}
return 0;
}