哈密尔顿回路: 从一个点出发,沿着边行走,经过每个顶点恰好一次,之后再回到出发点
欧拉回路: 从一个点出发,沿着边行走,经过每个边恰好一次,之后再回到出发点
欧拉回路性质: 对于无向连通图, 每个点的度是偶数 <-----> 图存在欧拉回路
判断图中有没有欧拉回路:
Hierholzer 算法:
# encoding = 'utf-8'
from copy import copy
from sortedcontainers import SortedSet as TreeSet
class CC:
def __init__(self, G):
self._G = G
self._visited = [False] * G.V
self._ccount = 0
for v in range(G.V):
if not self._visited[v]:
self._dfs_recursive(v)
self._ccount += 1
def _dfs_recursive(self, v):
self._visited[v] = True
for w in self._G.adj(v):
if not self._visited[w]:
self._dfs_recursive(w)
@property
def ccount(self):
return self._ccount
class Graph:
def __init__(self, filename):
self._filename = filename
lines = None
with open(filename, 'r') as f:
lines = f.readlines()
if not lines:
raise ValueError('Expected something from input file!')
# lines[0] -> V E
self._V, self._E = (int(i) for i in lines[0].split())
if self._V < 0:
raise ValueError('V must be non-negative')
if self._E < 0:
raise ValueError('E must be non-negative')
# size V list of TreeSet
self._adj = [TreeSet([]) for _ in range(self._V)]
for each_line in lines[1:]:
a, b = (int(i) for i in each_line.split())
self.validate_vertex(a)
self.validate_vertex(b)
if a == b:
raise ValueError('Self-Loop is detected!')
if b in self._adj[a]:
raise ValueError('Paralles edges are detected!')
self._adj[a].add(b)
self._adj[b].add(a)
@property
def V(self):
return self._V
@property
def E(self):
return self._E
def has_edge(self, v, w):
self.validate_vertex(v)
self.validate_vertex(w)
return w in self._adj[v]
def adj(self, v):
self.validate_vertex(v)
return self._adj[v]
def degree(self, v):
return len(self.adj(v))
def remove_edge(self, v, w):
self.validate_vertex(v)
self.validate_vertex(w)
if w in self._adj[v]:
self._adj[v].remove(w)
if v in self._adj[w]:
self._adj[w].remove(v)
def validate_vertex(self, v):
if v < 0 or v >= self._V:
raise ValueError('vertex ' + str(v) + ' is invalid')
def __str__(self):
res = ['V = {}, E = {}'.format(self._V, self._E)]
for v in range(self._V):
res.append('{}: {}'.format(v, ' '.join(str(w) for w in self._adj[v])))
return '\n'.join(res)
def __repr__(self):
return self.__str__()
def __copy__(self):
return Graph(self._filename)
class EulerLoop:
def __init__(self, G):
self._G = G
def has_euler_loop(self):
cc = CC(self._G)
if cc.ccount > 1:
return False
for v in range(self._G.V()):
if self._G.degree(v) % 2 != 0:
return False
return True
def result(self):
res = []
if not self.has_euler_loop:
return res
g = copy(self._G)
stack = []
currv = 0
stack.append(currv)
while stack:
if g.degree(currv) != 0:
stack.append(currv)
# 模拟一个iterator
w = next(self._iter_next_adj(g.adj(currv)))
g.remove_edge(currv, w)
currv = w
else:
# 此时说明找到了一个环
res.append(currv)
currv = stack.pop()
return res
def _iter_next_adj(self, adj):
yield from sorted(adj)
if __name__ == '__main__':
filename = 'g.txt'
g = Graph(filename)
eluer_loop = EulerLoop(g)
print(eluer_loop.result())
filename = 'g2.txt'
g = Graph(filename)
eluer_loop = EulerLoop(g)
print(eluer_loop.result())
g.txt
5 6
0 1
0 2
1 2
2 3
2 4
3 4
g2.txt
11 15
0 1
0 3
1 2
1 4
1 5
2 5
3 4
4 5
4 6
5 7
6 7
7 8
7 9
8 10
9 10