对于无向图,如果删除了一 个顶点,(顶点邻边也删除) 整个图连通分量数量变化,则这个顶点称为割点
# encoding = 'utf-8'
import sys
sys.path.append("..")
from sortedcontainers import SortedSet as TreeSet
class AdjSet:
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 AdjSet(self._filename)
class FindCutPoints:
def __init__(self, G):
self._G = G
self._visited = [False] * G.V
self._ord = [-1] * G.V
self._low = [- 1] * G.V
self._cnt = 0
self._res = set()
for v in range(G.V):
if not self._visited[v]:
self._dfs(v, v)
def _dfs(self, v, parent):
self._visited[v] = True
self._ord[v] = self._cnt
self._low[v] = self._ord[v]
self._cnt += 1
child = 0
for w in self._G.adj(v):
if not self._visited[w]:
self._dfs(w, v)
self._low[v] = min(self._low[v], self._low[w])
if v != parent and self._low[w] >= self._ord[v]:
self._res.add(v)
child += 1
if v == parent and child > 1:
self._res.add(v)
elif w != parent:
self._low[v] = min(self._low[v], self._low[w])
@property
def result(self):
return list(self._res)
if __name__ == '__main__':
filename = 'g.txt'
g = AdjSet(filename)
find_cut_points = FindCutPoints(g)
print(find_cut_points.result)