如何寻找欧拉回路、欧拉通路(套圈法)
传说中的套圈法。
算法思想的朴素表达
对于欧拉图,从一个节点出发,随便往下走(走过之后需要标记一下,下次就不要来了),必然也在这个节点终止(因为除了起始节点,其他节点的度数都是偶数,只要能进去就能出来)。这样就构成了一个圈,但因为是随便走的,所以可能会有些边还没走过就回来了。我们就从终止节点逆着往前查找,直到找到第一个分叉路口,然后从这个节点出发继续上面的步骤,肯定也是可以找到一条回到这个点的路径的,这时我们把两个圈连在一起。当你把所有的圈都找出来后,整个欧拉回路的寻找就完成了。
寻找欧拉回路时,起始节点是可以任意选择的。如果是有基度顶点要寻找欧拉通路,则从基度顶点出发就好了,上述步骤依然有效。
算法思想的书面表达
一个解决此类问题基本的想法是从某个节点开始,然后查出一个从这个点出发回到这个点的环路径。现在,环已经建立,这种方法保证每个点都被遍历.如果有某个点的边没有被遍历就让这个点为起点,这条边为起始边,把它和当前的环衔接上。这样直至所有的边都被遍历。这样,整个图就被连接到一起了。
更正式的说,要找出欧拉路径,就要循环地找出出发点。按以下步骤:
任取一个起点,开始下面的步骤
如果该点没有相连的点,就将该点加进路径中然后返回。
如果该点有相连的点,就列一张相连点的表然后遍历它们直到该点没有相连的点。(遍历一个点,删除一个点)
处理当前的点,删除和这个点相连的边, 在它相邻的点上重复上面的步骤,把当前这个点加入路径中.
下面是伪代码:
# circuit is a global array
find_euler_circuit
circuitpos = 0
find_circuit(node 1)
# nextnode and visited is a local array
# the path will be found in reverse order
find_circuit(node i)
if node i has no neighbors then
circuit(circuitpos) = node i
circuitpos = circuitpos + 1
else
while (node i has neighbors)
pick a random neighbor node j of node i
delete_edges (node j, node i)
find_circuit (node j)
circuit(circuitpos) = node i
circuitpos = circuitpos + 1
要找欧拉路径, 只要简单的找出一个度为奇数的节点,然后调用 find_circuit 就可以了.
一点点代码(pku 2337里面截过来的,具体请看http://blog.csdn.net/logic_nut/archive/2009/08/22/4473174.aspx)
view plaincopy to clipboardprint?
//主函数中调用下面这个函数
euler(start, -1); //因为直接从start出发,所以第二个参数用-1代替,输出的时候要忽略掉。
//euler函数就是用的传说中的套圈法,是一个迭代的过程
//npath是一个全局变量,记录已经找到的边的数量
//边存储在adj[]中,是用数组实现的链表结构(就跟很多hash那样,首节点的数据域不用,只有指针部分有效)
void euler(int cur, int edgeN) //cur当前到达的节点 edgeN上一被选择的边,即上一个节点通过edgeN到达的cur
{
int i;
while(adj[cur].nxt != -1)
{
i=adj[cur].nxt;
adj[cur].nxt=adj[i].nxt;//相当于是删除掉使用了的边
euler(adj[i].end,i);
}
path[npath++] = edgeN; //后序记录,如果要保持搜索时候边的优先级,则逆向输出
}