1、基本概念:
(1)定义
欧拉通路 (欧拉迹)—通过图中每条边一次且仅一次,并且过每一顶点的通路。
欧拉回路 (欧拉闭迹)—通过图中每条边一次且仅一次,并且过每一顶点的回路。
欧拉图—存在欧拉回路的图。欧拉图就是从一顶出发每条边恰通过一次又能回到出发顶点的那种图,即不重复的行遍所有的边再回到出发点。
通路和回路-称vie1e2…envj为一条从 vi到 vj且长度为n的通路,其中长度是指通路中边的条数.称起点和终点相同的通路为一条回路。
简单图-不含平行边和自回路的图。
混合图-既有有向边,也有无向边的图
平凡图-仅有一个结点的图
完全图-有n个结点的且每对结点都有边相连的无向简单图,称为无向完全图;有n个结点的且每对结点之间都有两条方向相反的边相连的有向简单图为有向完全图。
(2)欧拉图的特征:
无向图
a)G有欧拉通路的充分必要条件为:G 连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)。
b)G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点。
有向图
a)D有欧拉通路:D连通,除两个顶点外,其余顶点的入度均等于出度,这两个特殊的顶点中,一个顶点的入度比出度大1,另一个顶点的入度比出度小1。
b)D有欧拉回路(D为欧拉图):D连通,D中所有顶点的入度等于出度。一个有向图是欧拉图,当且仅当该图所有顶点度数都是0。
2、弗罗莱(Fleury)算法思想-解决欧拉回路
Fleury算法:
任取v0∈V(G),令P0=v0;
设Pi=v0e1v1e2…ei vi已经行遍,按下面方法从中选取ei+1:
(a)ei+1与vi相关联;
(b)除非无别的边可供行遍,否则ei+1不应该为Gi=G-{e1,e2, …, ei}中的桥(所谓桥是一条删除后使连通图不再连通的边);
(c)当(b)不能再进行时,算法停止。
可以证明,当算法停止时所得的简单回路Wm=v0e1v1e2….emvm(vm=v0)为G中的一条欧拉回路,复杂度为O(e*e)。
3、欧拉算法C语言描述
void DFS(Graph &G,SqStack &S,int x,int t)
{
k=0;//一个标志,来标记当前访问的节点是否还有邻接边可供访问
Push(S,x); //将本次遍历边所经由的点入栈
for(i=t;i if(G[i][x]>0) { k=1; G[i][x]=0; G[x][i]=0; //此边已访问,删除此边 DFS(G,S,i,0);//寻找下一条关联的边,本次找到的是与x关联的i,在 //下一层中将寻找与i关联的边 break; }//if,for if(k==0) //如果k=0,说明与当前顶点关联的边已穷尽 { Pop(S); GetTop(S,m); G[x][m]=1;G[m][x]=1;//恢复在上一层中被删除的边 a=x+1;//如果可能的话,从当前节点的下一条关联边开始搜寻 if(StackLength(S)!=e)//继续搜寻,边还没有全部遍历完 { Pop(S); //还原到上一步去 DFS(G,S,m,a);// }//if else //搜寻完毕,将最后节点也入栈 Push(S,x); }//if }//DFS void Euler(Graph &G,int x) { //G是存储图的邻接矩阵,都处理成无向图形式,值为1代表有边,0代表无边,不包括自回路,x是出发点 InitStack(S);//用来存放遍历边时依次走过的顶点 DFS(G,S,x,0);//深度优先遍历查找,0是指查询的起点 //输出 while(!StackEmpty(S)) { GetTop(S,m); printf("->v%d",m); Pop(S); }//while }//Euler 如下为算法的图示动态过程: 4、欧拉算法的C实现 #include "SqStack.h" //堆栈的常见操作 #include "Queue.h"//队列的常见操作 typedef int Graph[200][200]; int v,e; void DFS(Graph &G,SqStack &S,int x,int t) { int k=0,i,m,a; Push(S,x); for(i=t;i if(G[i][x]>0) { k=1; G[i][x]=0; //删除此边 G[x][i]=0; DFS(G,S,i,0); break; }//if,for if(k==0) { Pop(S); GetTop(S,m); G[x][m]=1;//恢复刚刚删除的边 G[m][x]=1; a=x+1;//从下一条边开始搜寻 if(StackLength(S)!=e) { Pop(S); DFS(G,S,m,a); }//if else Push(S,x); }//if }//DFS int BFSTest(Graph G) { int a[200],x,i,k=0; LinkQueue Q; InitQueue(Q); EnQueue(Q,0); for(i=0;i a[i]=0; a[0]=1; while(!QueueEmpty(Q)) { DeQueue(Q,x); for(i=0;i if(G[x][i]>0) if(a[i]!=1) { a[i]=1; EnQueue(Q,i); }//if }//while for(i=0;i if(a[i]==0) { k=1; break; } if(k==1) return 0; else return 1; } void Euler(Graph &G,int x) { int m; SqStack S; InitStack(S); DFS(G,S,x,0); printf("该图的一个欧拉回路为:"); while(!StackEmpty(S)) { GetTop(S,m); printf("->v%d",m); Pop(S); }//while } void InputM1(Graph &G) { int h,z; printf("Please input 顶点数和边数\n"); scanf("%d",&v); scanf("%d",&e); for(int i=0;i for(int j=0;j G[i][j]=0; printf("please int the 邻接矩阵的值(起点(数字) 终点(数字)):\n"); for(int i=0;i { scanf("%d",&h); scanf("%d",&z); G[h-1][z-1]=1; G[z-1][h-1]=1; }//for }//InputM1 int main() { int i,j,sum,k=0; Graph G; InputM1(G); if(BFSTest(G)==0) { printf("该图不是连通图!\n"); exit(0); }//if for(i=0;i { sum=0; for(j=0;j sum+=G[i][j]; if(sum%2==1) { k=1; break; }//if }//for if(k==1) printf("该图不存在欧拉回路!\n"); else Euler(G,0); //从那个点出发 return 1; } 顶点数5,边数为6 相关联的点1 2 1 3 2 5 4 2 3 2 4 5 运行结果(略) 5、小常识:欧拉算法的起由及一笔画问题 七桥问题:18世纪著名古典数学问题之一。在哥尼斯堡的一个公园里,有七座桥将普雷格尔河中两个岛及岛与河岸连接起来(如图)。问是否可能从这四块陆地中任一块出发,恰好通过每座桥一次,再回到起点?欧拉于1736年研究并解决了此问题,他把问题归结为如下右图的“一笔画”问题,证明上述走法是不可能的。 ⒈凡是由偶点组成的连通图,一定可以一笔画成。画时可以把任一偶点为起点,最后一定能以这个点为终点画完此图。 ⒉凡是只有两个奇点的连通图(其余都为偶点),一定可以一笔画成。画时必须把一个奇点为起点,另一个奇点终点。 ⒊其他情况的图都不能一笔画出。(奇点数除以二便可算出此图需几笔画成。) 6、欧拉回路和Hamilton(汉密尔顿)回路 汉密尔顿图与欧拉图的区别只在于,边与顶点的区别,欧拉图是每边经过一次,汉密尔顿图是每顶经过一次。