从一个节点出发的一条路径每条边只经过一次,则把这条路称作欧拉道路。
欧拉道路存在着这样的性质除起点和终点以外所有经过的节点的出度和入度相等,也就是说除起点和终点以外的节点的度都为偶数。
同时一个无向图是连通的,且最多存在两个奇点,则一定存在欧拉道路,并且如果是有两个奇点,欧拉道路一定是从一个奇点出发到达另外一个奇点,如果没有奇点,则从任何一个节点出发然后最终回到该点(欧拉回路),奇点为一的情况是不存在的。
对于存在欧拉道路的情况,最多只能有两个点的入度不等于出度,而且必须是其中一个节点的入度比出度大一,而另一个节点相反入度比出度少一,相差的这个一也就分别作起点和终点。前提是有向图忽略方向后图是连通图。
概括的说:
判断是否存在欧拉道路的方法是:忽略方向后图连通并且奇点数量为0或者2.
判断是否存在欧拉回路的方法是:忽略方向后图连通并且奇点数量为0。
判断图连通与否可以通过dfs从一个节点出发遍历整个忽略方向后的图,并且统计节点数量,若dfs统计出的节点数量和总的节点数量不同则说明图不连通。还有一种方法是并查集。
获取欧拉道路的算法:(一个图完全可能存在多条欧拉道路,这里的只求一条):
- struct edge{
- int begin,end;
- edge(int a=0,int b=0):begin(a),end(b){}
- };
- int N; //总的节点数量
- bool flag[MaxN][MaxN]; //标记两个节点间是否直接连通
- bool vis[MaxN][MaxN]; //标记连接两个节点的边是否被访问过
- stack<edge> ans; //存放路径中的各条边
- //同时适用于欧拉道路和回路
- //初次调用参数应该为起点坐标
- void euler(int here){
- for(int i=0;i<N;i++){
- if(flag[here][i]&&!vis[here][i]){
- vis[here][i]=vis[i][here]=true;
- euler(i);
- ans.push(edge(here,i));
- }
- }
- }
关于为什么统计边的信息要放在递归之后?既然能一笔画完为什么不直接打印?看下面的图。
图只有两个奇点1和10,也就是说存在一条欧拉道路,但图中存在一条桥边2-7,若在访问3456节点之前就去访问了7节点,则按照dfs打印出来的欧拉路径为1-2-7-10-9-8-7-12-11-10-4-3-6-5,但10和4之间并没有直接连通,也就是说虽然任然访问了全部边但顺序出错。
其实上面这个错的顺序是由1-2-7-10-9-8-7-12-11-10和一条欧拉回路4-3-6-5这两部分组成的,因为奇点最多为2并且必须要分别作为起点和终点的性质,类似1-2-7-10-9-8-7-12-11-10这样的欧拉路径在欧拉图中最多存在一条(也就是有桥的情况下),删除掉这条路径上的全部边之后剩下的只能是欧拉回路或者为空,访问有可能一次就直接访问完了全部边,但也有可能先访问桥边,造成结果错误。
利用函数入栈先进后出的性质,若先访问了桥,因为欧拉回路有从任何一点出发最终一定回到起点的性质,如果先访问桥边最后就会剩下一些欧拉回路,如果最后访问桥边就不会剩下欧拉回路,先访问完欧拉回路再访问桥边能访问到最多的节点,利用栈倒序就是为了达到类似效果。