什么是歐拉路徑?歐拉路徑就是一條能夠不重不漏地經過圖上的每一條邊的路徑,即小學奧數中的一筆畫問題。而若這條路徑的起點和終點相同,則將這條路徑稱為歐拉回路。
如何判斷一個圖是否有歐拉路徑呢?顯然,與一筆畫問題相同,一個圖有歐拉路徑需要以下幾個條件:
- 首先,這是一個連通圖
- 若是無向圖,則這個圖的度數為奇數的點的個數必須是0或2;若是有向圖,則要么所有點的入度和出度相等,要么有且只有兩個點的入度分別比出度大1和少1
上面這兩個條件很好證明。查找歐拉路徑前,必須先保證該圖滿足以上兩個條件,否則直接判誤即可。
查找歐拉路徑的算法有Fluery算法和Hierholzer算法。下面介紹一下Hierholzer算法。
算法流程:
- 對於無向圖,判斷度數為奇數的點的個數,若為0,則設任意一點為起點,若為2,則從這2個點中任取一個作為起點;對於有向圖,判斷入度和出度不同的點的個數,若為0,則設任意一點為起點,若為2,則設入度比出度小1的點為起點,另一點為終點。具體起點的選擇要視題目要求而定。
- 從起點開始進行遞歸:對於當前節點x,掃描與x相連的所有邊,當掃描到一條(x,y)時,刪除該邊,並遞歸y。掃描完所有邊后,將x加入答案隊列。
- 倒序輸出答案隊列。(因為這里是倒序輸出,我們可以用棧來存儲答案,當然用雙端隊列也可以)
解析:
從起點開始,每一次執行遞歸函數,相當於模擬一筆畫的過程。遞歸的邊界顯然就是路徑的終點,對於一個有歐拉路徑的圖,此時圖上的所有邊都已被刪除,自然就不能繼續遞歸。由於存儲答案是在遍歷以后進行的,答案存儲也就是倒序的,因此要倒序輸出答案。
代碼:
#include<iostream>
#include<stack>
using namespace std;
const int N=500;
int n,tot,c=N,jp[N],cnt[N],edge[N][N];
char a,b;
stack<int> q;
void dfs(int now)
{
for(int i=1;i<=N;i++)
if(edge[now][i]==1)
{
edge[now][i]--,edge[i][now]--;
dfs(i);
}
q.push(now);//加入答案隊列
}//算法過程
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a>>b;
c=min(c,a);
c=min(c,b);
edge[a][b]++,edge[b][a]++;
cnt[a]++;
cnt[b]++;//統計每個節點的度數
}
for(int i=1;i<=N;i++)
if(cnt[i]%2==1)
jp[tot++]=i;//找出度數為奇數的節點
if(tot!=2 && tot)
{
cout<<"No Solution";
return 0;
}//若該圖沒有歐拉路徑則判誤
int stat;
if(tot)
stat=min(jp[0],jp[1]);
else
stat=c;//找出起點
dfs(stat);
while(!q.empty())
{
char ct=q.top();
cout<<ct;
q.pop();
}//倒序輸出
return 0;
}
在上面的代碼中,找出的是起點字典序最小的歐拉路徑,具體情況應視題意而定。
習題:
2019.4.16 於廈門外國語學校石獅分校