用于打印欧拉路径或电路的 Fleury 算法
欧拉路径是图中的一条路径,它只访问每条边一次。欧拉回路是在同一顶点开始和结束的欧拉路径。
我们强烈建议先阅读以下关于欧拉路径和电路的文章。
https://www.geeksforgeeks.org/eulerian-path-and-circuit/
在上面提到的帖子中,我们讨论了找出给定图是否是欧拉图的问题。在这篇文章中,讨论了一种打印欧拉轨迹或电路的算法。
以下是用于打印欧拉轨迹或循环的 Fleury 算法(来源 Ref1)。
1.确保图形有 0 个或 2 个奇数顶点。
2.如果有0个奇数顶点,从任何地方开始。如果有 2 个奇数顶点,则从其中一个开始。
3.一次沿着一条边走。如果您在桥接和非桥接之间进行选择,请始终选择非桥接。
4.当边缘用完时停止。
这个想法是, “不要烧桥” ,这样我们就可以回到顶点并遍历剩余的边。例如,让我们考虑下图。
有两个奇数度的顶点,“2”和“3”,我们可以从它们中的任何一个开始路径。让我们从顶点'2'开始游览。
从顶点 '2' 出来的边有 3 条,选择哪一条?我们不选择边“2-3”,因为那是一座桥(我们将无法回到“3”)。我们可以选择剩下的两条边中的任何一条。假设我们选择“2-0”。我们移除这条边并移动到顶点'0'。
顶点'0'只有一条边,所以我们选择它,删除它并移动到顶点'1'。欧拉巡回赛变为“2-0 0-1”。
顶点'1'只有一条边,所以我们选择它,删除它并移动到顶点'2'。欧拉巡回赛变成'2-0 0-1 1-2'
同样,从顶点 2 开始只有一条边,所以我们选择它,将其移除并移动到顶点 3。欧拉之旅变为 '2-0 0-1 1-2 2-3'
没有更多的边缘了,所以我们在这里停下来。最终巡回赛是“2-0 0-1 1-2 2-3”。
有关更多示例,请参见 this 和 this。
以下是上述算法的 C++ 实现。在下面的代码中,假设给定的图具有欧拉轨迹或回路。主要重点是打印欧拉轨迹或电路。我们可以使用 isEulerian() 首先检查给定图中是否存在欧拉轨迹或回路。
我们首先找到必须是奇数顶点的起点(如果有奇数顶点)并将其存储在变量“u”中。如果有零个奇数顶点,我们从顶点'0'开始。我们调用 printEulerUtil() 来打印以 u 开头的欧拉之旅。我们遍历u的所有相邻顶点,如果只有一个相邻顶点,我们立即考虑。如果有多个相邻顶点,则仅当边 uv 不是桥时,我们才考虑相邻的 v。如何查找给定的边缘是否是桥?我们计算从 u 可到达的顶点数。我们删除边 uv 并再次计算 u 中可达顶点的数量。如果可到达顶点的数量减少,那么边 uv 就是一座桥。要计算可达顶点,我们可以使用 BFS 或 DFS,我们在上面的代码中使用了 DFS。函数DFSCount(u) 返回从 u 可到达的顶点数。
一旦处理了一条边(包括在欧拉之旅中),我们就将其从图中删除。为了移除边,我们将邻接列表中的顶点条目替换为 -1。请注意,简单地删除节点可能不起作用,因为代码是递归的,并且父调用可能位于邻接列表的中间。
C++
// A C++ program print Eulerian Trail in a given Eulerian or
// Semi-Eulerian Graph
#include
#include
#include
#include
using namespace std;
// A class that represents an undirected graph
class Graph {
int V; // No. of vertices
list* adj; // A dynamic array of adjacency lists
public:
// Constructor and destructor
Graph(int V)
{
this->V = V;
adj = new list[V];
}
~Graph() { delete[] adj; }
// functions to add and remove edge
void addEdge(int u, int v)
{
adj[u].push_back(v);
adj[v].push_back(u);
}
void rmvEdge(int u, int v);
// Methods to print Eulerian tour
void printEulerTour();
void printEulerUtil(int s);
// This function returns count of vertices reachable
// from v. It does DFS
int DFSCount(int v, bool visited[]);
// Utility function to check if edge u-v is a valid next
// edge in Eulerian trail or circuit
bool isValidNextEdge(int u, int v);
};
/* The main function that print Eulerian Trail. It first
finds an odd degree vertex (if there is any) and then
calls printEulerUtil() to print the path */
void Graph::printEulerTour()
{
// Find a vertex with odd degree
int u = 0;
for (int i = 0; i < V; i++)
if (adj[i].size() & 1) {
u = i;
break;
}
// Print tour starting from oddv
printEulerUtil(u);
cout << endl;
}
// Print Euler tour starting from vertex u
void Graph::printEulerUtil(int u)
{
// Recur for all the vertices adjacent to this vertex
list::iterator i;
for (i = adj[u].begin(); i != adj[u].end(); ++i) {
int v = *i;
// If edge u-v is not removed and it's a a valid
// next edge
if (v != -1 && isValidNextEdge(u, v)) {
cout << u << "-" << v << " ";
rmvEdge(u, v);
printEulerUtil(v);
}
}
}
// The function to check if edge u-v can be considered as
// next edge in Euler Tout
bool Graph::isValidNextEdge(int u, int v)
{
// The edge u-v is valid in one of the following two
// cases:
// 1) If v is the only adjacent vertex of u
int count = 0; // To store count of adjacent vertices
list::iterator i;
for (i = adj[u].begin(); i != adj[u].end(); ++i)
if (*i != -1)
count++;
if (count == 1)
return true;
// 2) If there are multiple adjacents, then u-v is not a
// bridge Do following steps to check if u-v is a bridge
// 2.a) count of vertices reachable from u
bool visited[V];
memset(visited, false, V);
int count1 = DFSCount(u, visited);
// 2.b) Remove edge (u, v) and after removing the edge,
// count vertices reachable from u
rmvEdge(u, v);
memset(visited, false, V);
int count2 = DFSCount(u, visited);
// 2.c) Add the edge back to the graph
addEdge(u, v);
// 2.d) If count1 is greater, then edge (u, v) is a
// bridge
return (count1 > count2) ? false : true;
}
// This function removes edge u-v from graph. It removes
// the edge by replacing adjacent vertex value with -1.
void Graph::rmvEdge(int u, int v)
{
// Find v in adjacency list of u and replace it with -1
list::iterator iv
= find(adj[u].begin(), adj[u].end(), v);
*iv = -1;
// Find u in adjacency list of v and replace it with -1
list::iterator iu
= find(adj[v].begin(), adj[v].end(), u);
*iu = -1;
}
// A DFS based function to count reachable vertices from v
int Graph::DFSCount(int v, bool visited[])
{
// Mark the current node as visited
visited[v] = true;
int count = 1;
// Recur for all vertices adjacent to this vertex
list::iterator i;
for (i = adj[v].begin(); i != adj[v].end(); ++i)
if (*i != -1 && !visited[*i])
count += DFSCount(*i, visited);
return count;
}
// Driver program to test above function
int main()
{
// Let us first create and test graphs shown in above
// figure
Graph g1(4);
g1.addEdge(0, 1);
g1.addEdge(0, 2);
g1.addEdge(1, 2);
g1.addEdge(2, 3);
g1.printEulerTour();
Graph g2(3);
g2.addEdge(0, 1);
g2.addEdge(1, 2);
g2.addEdge(2, 0);
g2.printEulerTour();
Graph g3(5);
g3.addEdge(1, 0);
g3.addEdge(0, 2);
g3.addEdge(2, 1);
g3.addEdge(0, 3);
g3.addEdge(3, 4);
g3.addEdge(3, 2);
g3.addEdge(3, 1);
g3.addEdge(2, 4);
g3.printEulerTour();
return 0;
}
Java
// A Java program print Eulerian Trail
// in a given Eulerian or Semi-Eulerian Graph
import java.util.ArrayList;
// An Undirected graph using
// adjacency list representation
public class Graph {
private int vertices; // No. of vertices
private ArrayList[] adj; // adjacency list
// Constructor
Graph(int numOfVertices)
{
// initialise vertex count
this.vertices = numOfVertices;
// initialise adjacency list
initGraph();
}
// utility method to initialise adjacency list
@SuppressWarnings("unchecked") private void initGraph()
{
adj = new ArrayList[vertices];
for (int i = 0; i < vertices; i++) {
adj[i] = new ArrayList<>();
}
}
// add edge u-v
private void addEdge(Integer u, Integer v)
{
adj[u].add(v);
adj[v].add(u);
}
// This function removes edge u-v from graph.
private void removeEdge(Integer u, Integer v)
{
adj[u].remove(v);
adj[v].remove(u);
}
/* The main function that print Eulerian Trail.
It first finds an odd degree vertex (if there
is any) and then calls printEulerUtil() to
print the path */
private void printEulerTour()
{
// Find a vertex with odd degree
Integer u = 0;
for (int i = 0; i < vertices; i++) {
if (adj[i].size() % 2 == 1) {
u = i;
break;
}
}
// Print tour starting from oddv
printEulerUtil(u);
System.out.println();
}
// Print Euler tour starting from vertex u
private void printEulerUtil(Integer u)
{
// Recur for all the vertices adjacent to this
// vertex
for (int i = 0; i < adj[u].size(); i++) {
Integer v = adj[u].get(i);
// If edge u-v is a valid next edge
if (isValidNextEdge(u, v)) {
System.out.print(u + "-" + v + " ");
// This edge is used so remove it now
removeEdge(u, v);
printEulerUtil(v);
}
}
}
// The function to check if edge u-v can be
// considered as next edge in Euler Tout
private boolean isValidNextEdge(Integer u, Integer v)
{
// The edge u-v is valid in one of the
// following two cases:
// 1) If v is the only adjacent vertex of u
// ie size of adjacent vertex list is 1
if (adj[u].size() == 1) {
return true;
}
// 2) If there are multiple adjacents, then
// u-v is not a bridge Do following steps
// to check if u-v is a bridge
// 2.a) count of vertices reachable from u
boolean[] isVisited = new boolean[this.vertices];
int count1 = dfsCount(u, isVisited);
// 2.b) Remove edge (u, v) and after removing
// the edge, count vertices reachable from u
removeEdge(u, v);
isVisited = new boolean[this.vertices];
int count2 = dfsCount(u, isVisited);
// 2.c) Add the edge back to the graph
addEdge(u, v);
return (count1 > count2) ? false : true;
}
// A DFS based function to count reachable
// vertices from v
private int dfsCount(Integer v, boolean[] isVisited)
{
// Mark the current node as visited
isVisited[v] = true;
int count = 1;
// Recur for all vertices adjacent to this vertex
for (int adj : adj[v]) {
if (!isVisited[adj]) {
count = count + dfsCount(adj, isVisited);
}
}
return count;
}
// Driver program to test above function
public static void main(String a[])
{
// Let us first create and test
// graphs shown in above figure
Graph g1 = new Graph(4);
g1.addEdge(0, 1);
g1.addEdge(0, 2);
g1.addEdge(1, 2);
g1.addEdge(2, 3);
g1.printEulerTour();
Graph g2 = new Graph(3);
g2.addEdge(0, 1);
g2.addEdge(1, 2);
g2.addEdge(2, 0);
g2.printEulerTour();
Graph g3 = new Graph(5);
g3.addEdge(1, 0);
g3.addEdge(0, 2);
g3.addEdge(2, 1);
g3.addEdge(0, 3);
g3.addEdge(3, 4);
g3.addEdge(3, 2);
g3.addEdge(3, 1);
g3.addEdge(2, 4);
g3.printEulerTour();
}
}
// This code is contributed by Himanshu Shekhar
Python3
# Python program print Eulerian Trail in a given Eulerian or Semi-Eulerian Graph
from collections import defaultdict
#This class represents an undirected graph using adjacency list representation
class Graph:
def __init__(self,vertices):
self.V= vertices #No. of vertices
self.graph = defaultdict(list) # default dictionary to store graph
self.Time = 0
# function to add an edge to graph
def addEdge(self,u,v):
self.graph[u].append(v)
self.graph[v].append(u)
# This function removes edge u-v from graph
def rmvEdge(self, u, v):
for index, key in enumerate(self.graph[u]):
if key == v:
self.graph[u].pop(index)
for index, key in enumerate(self.graph[v]):
if key == u:
self.graph[v].pop(index)
# A DFS based function to count reachable vertices from v
def DFSCount(self, v, visited):
count = 1
visited[v] = True
for i in self.graph[v]:
if visited[i] == False:
count = count + self.DFSCount(i, visited)
return count
# The function to check if edge u-v can be considered as next edge in
# Euler Tour
def isValidNextEdge(self, u, v):
# The edge u-v is valid in one of the following two cases:
# 1) If v is the only adjacent vertex of u
if len(self.graph[u]) == 1:
return True
else:
'''
2) If there are multiple adjacents, then u-v is not a bridge
Do following steps to check if u-v is a bridge
2.a) count of vertices reachable from u'''
visited =[False]*(self.V)
count1 = self.DFSCount(u, visited)
'''2.b) Remove edge (u, v) and after removing the edge, count
vertices reachable from u'''
self.rmvEdge(u, v)
visited =[False]*(self.V)
count2 = self.DFSCount(u, visited)
#2.c) Add the edge back to the graph
self.addEdge(u,v)
# 2.d) If count1 is greater, then edge (u, v) is a bridge
return False if count1 > count2 else True
# Print Euler tour starting from vertex u
def printEulerUtil(self, u):
#Recur for all the vertices adjacent to this vertex
for v in self.graph[u]:
#If edge u-v is not removed and it's a a valid next edge
if self.isValidNextEdge(u, v):
print("%d-%d " %(u,v)),
self.rmvEdge(u, v)
self.printEulerUtil(v)
'''The main function that print Eulerian Trail. It first finds an odd
degree vertex (if there is any) and then calls printEulerUtil()
to print the path '''
def printEulerTour(self):
#Find a vertex with odd degree
u = 0
for i in range(self.V):
if len(self.graph[i]) %2 != 0 :
u = i
break
# Print tour starting from odd vertex
print ("\n")
self.printEulerUtil(u)
# Create a graph given in the above diagram
g1 = Graph(4)
g1.addEdge(0, 1)
g1.addEdge(0, 2)
g1.addEdge(1, 2)
g1.addEdge(2, 3)
g1.printEulerTour()
g2 = Graph(3)
g2.addEdge(0, 1)
g2.addEdge(1, 2)
g2.addEdge(2, 0)
g2.printEulerTour()
g3 = Graph (5)
g3.addEdge(1, 0)
g3.addEdge(0, 2)
g3.addEdge(2, 1)
g3.addEdge(0, 3)
g3.addEdge(3, 4)
g3.addEdge(3, 2)
g3.addEdge(3, 1)
g3.addEdge(2, 4)
g3.printEulerTour()
#This code is contributed by Neelam Yadav
C#
// A C# program print Eulerian Trail
// in a given Eulerian or Semi-Eulerian Graph
using System;
using System.Collections.Generic;
// An Undirected graph using
// adjacency list representation
class Graph
{
private int vertices; // No. of vertices
private List[] adj; // adjacency list
// Constructor
Graph(int numOfVertices)
{
// initialise vertex count
this.vertices = numOfVertices;
// initialise adjacency list
initGraph();
}
// utility method to initialise adjacency list
private void initGraph()
{
adj = new List[vertices];
for (int i = 0; i < vertices; i++)
{
adj[i] = new List();
}
}
// add edge u-v
private void addEdge(int u, int v)
{
adj[u].Add(v);
adj[v].Add(u);
}
// This function removes edge u-v from graph.
private void removeEdge(int u, int v)
{
adj[u].Remove(v);
adj[v].Remove(u);
}
/* The main function that print Eulerian Trail.
It first finds an odd degree vertex (if there
is any) and then calls printEulerUtil() to
print the path */
private void printEulerTour()
{
// Find a vertex with odd degree
int u = 0;
for (int i = 0; i < vertices; i++)
{
if (adj[i].Count % 2 == 1)
{
u = i;
break;
}
}
// Print tour starting from oddv
printEulerUtil(u);
Console.WriteLine();
}
// Print Euler tour starting from vertex u
private void printEulerUtil(int u)
{
// Recur for all the vertices
// adjacent to this vertex
for (int i = 0; i < adj[u].Count; i++)
{
int v = adj[u][i];
// If edge u-v is a valid next edge
if (isValidNextEdge(u, v))
{
Console.Write(u + "-" + v + " ");
// This edge is used so remove it now
removeEdge(u, v);
printEulerUtil(v);
}
}
}
// The function to check if edge u-v can be
// considered as next edge in Euler Tout
private bool isValidNextEdge(int u, int v)
{
// The edge u-v is valid in one of the
// following two cases:
// 1) If v is the only adjacent vertex of u
// ie size of adjacent vertex list is 1
if (adj[u].Count == 1)
{
return true;
}
// 2) If there are multiple adjacents, then
// u-v is not a bridge Do following steps
// to check if u-v is a bridge
// 2.a) count of vertices reachable from u
bool[] isVisited = new bool[this.vertices];
int count1 = dfsCount(u, isVisited);
// 2.b) Remove edge (u, v) and after removing
// the edge, count vertices reachable from u
removeEdge(u, v);
isVisited = new bool[this.vertices];
int count2 = dfsCount(u, isVisited);
// 2.c) Add the edge back to the graph
addEdge(u, v);
return (count1 > count2) ? false : true;
}
// A DFS based function to count reachable
// vertices from v
private int dfsCount(int v, bool[] isVisited)
{
// Mark the current node as visited
isVisited[v] = true;
int count = 1;
// Recur for all vertices adjacent
// to this vertex
foreach(int i in adj[v])
{
if (!isVisited[i])
{
count = count + dfsCount(i, isVisited);
}
}
return count;
}
// Driver Code
public static void Main(String []a)
{
// Let us first create and test
// graphs shown in above figure
Graph g1 = new Graph(4);
g1.addEdge(0, 1);
g1.addEdge(0, 2);
g1.addEdge(1, 2);
g1.addEdge(2, 3);
g1.printEulerTour();
Graph g2 = new Graph(3);
g2.addEdge(0, 1);
g2.addEdge(1, 2);
g2.addEdge(2, 0);
g2.printEulerTour();
Graph g3 = new Graph(5);
g3.addEdge(1, 0);
g3.addEdge(0, 2);
g3.addEdge(2, 1);
g3.addEdge(0, 3);
g3.addEdge(3, 4);
g3.addEdge(3, 2);
g3.addEdge(3, 1);
g3.addEdge(2, 4);
g3.printEulerTour();
}
}
// This code is contributed by PrinciRaj1992
输出:
2-0 0-1 1-2 2-3
0-1 1-2 2-0
0-1 1-2 2-0 0-3 3-4 4-2 2-3 3-1