歐拉通路: 通過圖中每條邊且只通過一次,並且經過每一頂點的通路
歐拉回路: 通過圖中每條邊且只通過一次,並且經過每一頂點的回路
有向圖的基圖:忽略有向圖所有邊的方向,得到的無向圖稱為該有向圖的基圖。
無向圖
設G是連通無向圖,則稱經過G的每條邊一次並且僅一次的路徑為歐拉通路;
如果歐拉通路是回路(起點和終點是同一個頂點),則稱此回路是歐拉回路
具有歐拉回路的無向圖G成為歐拉圖
有向圖
(1)設D是有向圖,D的基圖連通,則稱經過D的每條邊一次並且僅有一次的有向路徑為 有向歐拉通路
(2)如果有向歐拉通路是有向回路,則稱此有向回路為 有向歐拉回路
(3)具有有向歐拉回路的圖D稱為有向歐拉圖
定理
無向圖G存在歐拉通路的充要條件是:G為連通圖,並且G僅有兩個奇度結點(度數為奇數的頂點)或者無奇度結點。
推論
(1) 當G是僅有兩個奇度結點的連通圖時,G的歐拉通路必以此兩個結點為端點;
(2)當G是無奇度結點的連通圖時,G必有歐拉回路
(3)G為歐拉圖(存在歐拉回路)的充分必要條件是 G為無奇度結點的連通圖
(有向圖) 定理
有向圖D存在歐拉通路的充要條件是:D為有向圖,D的基圖連通,並且所有頂點的出度與入度相等;或者 除兩個頂點外,其余頂點的出度與入度都相等,而這兩個頂點中一個頂點的出度與入度之差為1,另一個頂點的出度與入度之差為-1.
推論
(1)當D除出、入度之差為1,-1的兩個頂點之外,其余頂點的出度與入度相等時,D的有向歐拉通路必以出、入度之差為1的頂點作為始點,以出、入度之差為-1的頂點作為終點。
(2)當D的所有頂點的出、入度都相等時,D中存在有向歐拉回路。
(3)有向圖D為有向歐拉圖的充要條件是 D的基圖為連通圖,並且所有頂點的出、入度都相等。
歐拉回路的求解
兩種方法:(1)DFS搜索 (Fleury)佛羅萊算法
(1)DFS搜索 思想求解歐拉回路的思路為:利用歐拉定理判斷出一個圖存在歐拉通路或歐拉回路后,選擇一個正確的起始頂點,用DFS算法遍歷所有的邊(每條邊只遍歷一次),遇到走不通就回退。在搜索前進方向上將遍歷過的邊按順序記錄下來。這組邊的排列就組成了一條歐拉通路或回路。
(2) (Fleury)佛羅萊算法
設G為一個無向歐拉圖,求G中一條歐拉回路的算法如下:
(1) 任取G中一頂點v0,令P0=v0;
(2)假設沿Pi=v0e1v1e2v2......eivi走到頂點vi,按下面方法從E(G)-{e1,e2,...,ei}中選ei+1。
ei+1與vi相關聯
除非無別的邊可供選擇,否則ei+1不應該是Gi=G-{e1,e2,...,ei}中的橋。
(3)當(2)不能再進行時算法停止。
可以證明的是,當算法停止時,所得到的簡單回路Pm=v0e1v1e2v2......emvm,(vm=v0)為G中一條歐拉回路。
備注知識:
設無向圖G(V,E)為連通圖,若邊集E1屬於E,在圖G中刪除E1中所有的邊后得到的子圖是不連通的,而刪除了E1的任一真子集后得到的子圖是連通圖,則稱E1是G的一個割邊集。若一條邊構成一個割邊集,則稱該邊為割邊,或橋
//歐拉路徑的輸出(此圖為無向圖)
#include<iostream>
using namespace std;
#define M 200
struct stack
{
int top,node[M];
}s; //頂點的棧結構
int Edge[M][M]; //鄰接矩陣
int n; //頂點個數
void dfs(int x) //這里的深度優先跟標准版有所區別,即不需要回溯
{
s.top++;
s.node[s.top]=x; //將即將要擴展的結點壓入棧中
for(int i=0;i<n;i++)
{
if(Edge[i][x]) //如果該節點還存在邊
{
Edge[i][x]=0;
Edge[x][i]=0; //刪除該邊,然后搜索另一結點
dfs(i);
break;
}
}
}
void Fleury(int x) //對頭節點使用Fleury算法 查找歐拉路徑
{
s.top=0;
s.node[s.top]=x;
while(s.top>=0)
{
int flag=0; //記錄當前結點是否有邊可以擴展
for(int i=0;i<n;i++)
{
if(Edge[i][s.top])
{
flag=1;
break;
}
}
if(!flag)
{
cout<<s.node[s.top]+1<<" "; //記錄時是從0--n-1,所以應該加1
s.top--; //結點輸出了,此結點出棧
}
else
{
s.top--; //因為dfs處理時,需要先進棧,所以這里要先出棧,然后再進棧
dfs(s.node[s.top+1]); //處理那個結點
}
}
cout<<endl;
}
int main()
{
int m,s,t; //邊數,讀入的邊的起點和終點
int degree,num,start; //每個頂點的度、基度頂點個數、歐拉回路的起點
cin>>n>>m; //n---頂點數 m---邊數
memset(Edge,0,sizeof(Edge));
for(int i=0;i<n;i++)
{
cin>>s>>t;
Edge[s-1][t-1]=1;
Edge[t-1][s-1]=1;
}
num=0;
start=0; //如果存在奇度頂點,則從奇度頂點出發,否則從頂點0出發
for(int i=0;i<n;i++)
{
degree=0;
for(int j=0;j<n;j++)
degree+=Edge[i][j];
if(degree%2)
{
num++;
start=i;
}
}
if(num==0||num==2) Fleury(start);
else cout<<"No Euler path"<<endl;
return 0;
}
