浅谈Fleury(佛罗莱)算法 欧拉回路(及路径)

首先复习一下欧拉回路(及路径)的几个基础概念与定理

First

欧拉回路:是指所有的边都只经过一次且仅一次,并且要走回到出发点的一条路径
欧拉路径:表示一条不需要回到出发点,但是必须经过所有的边且都只经过一次的路径

Second

无向图存在欧拉回路的充要条件是所有的点的度数均为偶数
无向图存在欧拉路径的充要条件是度数为奇数的点的数量为 0 个或者2

有向图存在欧拉回路的充要条件是所有的点的出度均等于入度
有向图存在欧拉路径的充要条件是:
①:欧拉回路的情况
②:所有点中出度比入度大 1 的点有一个,入度比出度大1的点有一个,不允许有大几个的情况

Third

定理:多笔画问题:如果一个无向图中有 2k 个奇数出度的顶点,则至少需要 k 笔画才能把其走完
证明:每次只能把两个奇数出度的顶点走完,因此对云2k个奇数出度的顶点至少需要k笔画


Fleury 算法用于解决欧拉回路的具体输出路径问题,在算法开始之前,我们先用一个 dfs 来判断这个图是否是一个联通块,然后再判断这个图中有奇数出度的点是否只有 0 个或者2个,如果是 0 个,则存在欧拉回路,如果是两个,则存在欧拉路径,对于欧拉回路,我们任意选择一个点作为dfs的第一个点,对于欧拉路径,我们选取两个奇数出度的点中之一来作为 dfs 的第一个点

我们在求取的时候,用这种数据结构

举个例子

这里写图片描述

比如说这个图,显然奇数出度的点为 1 2,于是我们选择一个点,比如说选择 1 <script type="math/tex" id="MathJax-Element-2689">1</script>,那么我们在dfs的过程中,按照dfs的顺序把点的编号放进栈中,比如我们的访问次序是12432,则把12432放入栈中,每经过一条边,就把这条边的正向边反向边打上标记表示这条路已经走过了,由于之前我们已经判过欧拉回路存在的充要条件了,所以请对这张图保持信心,一定是可以找到欧拉回路的,于是我们的算法流程就是:
先把1放到栈中,然后把1-2的边的正向边反向边打上标记,表示已经走过了,然后再把2放到栈中,然后走2-4,打标记,4入栈,走4-3,打标记,3入栈,走3-2,打标机,2入栈,然后我们去走2的时候发现2已经无路可走了,她能走的所有边已经被打上了标记,也就是说这个点已经没有办法出去了,那么什么样的点进去了出不来呢?显然就是我们的奇数出度的点,于是我们在这里把栈顶输出,然后pop出去,然后回溯,每回溯到一个点都判断这个点是否还能走其他边,如果不能走的话,我们就输出这个点,然后再回溯,一直到一个点有其他边可以走,我们就把这个pop,但是不输出,然后再重新从这个点开始dfs

比如说这幅图中,我们在dfs了2及右边之后,左边的1由于还有边可以走,于是不输出,从1再开始dfs,最后输出的序列就是2 , 4 , 3,2,1,5 , 6 , 1

代码实现:

void dfs( int u ) {
    sta.push(u);
    for( register int i = head[u]; i; i = line[i].nxt ) {
        if( vis[i] ) continue;
        vis[i] = vis[ i ^ 1 ] = true;
        dfs( line[i].to );
        break;
    }
}

void fleury( int st ) {
    sta.push(st);
    while(!sta.empty() ) {
        int flag = 0, u = sta.top(); sta.pop();
        for( register int i = head[u]; i; i = line[i].nxt) {
            if( vis[i] ) continue;
            flag = 1; break;
        }
        if( !flag ) printf( "%d\n", u );
        else        dfs(u);
    }
}
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值