欧拉回路

一、引言

欧拉回路问题是图论中最古老的问题,诞生于18世纪欧洲的科尼斯堡问题

二、定义

(G=(V,E))是一个图。

欧拉回路:图G中经过每条边一次并且仅一次的回路。

欧拉路径:图G中经过每条边一次并且仅一次的路径。

欧拉图:存在欧拉回路的图。

半欧拉图:存在欧拉路径但不存在欧拉回路的图。

三、性质与定理

假设下面讨论的图G不存在孤立点,否则,先将孤立点从图中删除。

对于无向图:

定理1:无向图G为欧拉图,当且仅当G为连通图且所有定点的度为偶数。

推论1:无向图G为半欧拉图,当且仅当G为连通图且除了两个顶点的度为奇数之外,其它所有顶点的度为偶数。

对于有向图:

定理2:有向图G为欧拉图,当且仅当G的基图连通,且所有顶点的入度等于出度。

推论2:有向图G为半欧拉图,当且仅当G的基图连通,且存在顶点u的入度比出度大1,v的入度比出度小1,其它所有顶点的入度等于出度。

基图:忽略有向图所有边的方向,得到的无向图成为该有向图的基图。

四、求欧拉回路

两种:Hierholzer(套圈)算法和Fluery算法,前者编程简单,时间复杂度更优,后者应用性更广泛,这里不深入分析。

Hierholzer算法:

#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
const int N=1025;
multiset<int> h[N];
int degree[N], road[N], k;

void dfs(int x)
{
    for(multiset<int>::iterator py=h[x].begin(); py!=h[x].end(); py=h[x].begin()){
        int y=*py;													 
        h[x].erase(py);												//删边x->y 
        h[y].erase(h[y].find(x));									//删边x->y
        dfs(y);														//从y递归 
    }
    road[k++]=x;													//往队列里插入答案 
}

int main(){
    int F, s=0, t=0;												//s起点,t终点 
    scanf("%d", &F);
    for(int i=1, a, b; i<=F; i++)	scanf("%d%d", &a, &b), degree[a]++, degree[b]++, h[a].insert(b), h[b].insert(a);
    for(int i=1; i<=1024; i++)										//注意模板题中栅栏结点的编号范围不是1~F 
    {
        if(degree[i]%2){											//发现奇点 
            if(!s)			s=i;
            else if(!t)		t=i;
            else 			return 0;								//发现奇点超过2个,无欧拉路径 
        }
	}
	if(s && !t)	return 0;											//只有1个奇点,无欧拉路径
	if(!s)	s=1;													//没有奇点,有欧拉回路,指定1为起点 
    dfs(s);
    for(k=k-1; k>=0; k--)	printf("%d
", road[k]);				//倒序输出答案,也可以不用road数组,使用一个stack 
    return 0;
}

效率分析:共插入了2E条边,dfs共遍历2E条边。使用multiset可便于快速查找最小值,查找,删除边的复杂度为lgn,故程序时间复杂度为O(Elgn)。

原文地址:https://www.cnblogs.com/lfyzoi/p/10789663.html