算法描述
算法的核心思想比较简单,与前三个算法都不同,它是把边按长度逆序排序,每次删除最长的边,直到图变成树为止。我举个例子,以下是图:
最长的是10,删掉:
接下来就是长度为9的两个了,删掉,没问题的:
接下来就是为8的,删除一个:
剩下的这个长度为8的可不能删除了,因为删除了就不连通了。那就还剩下长度为7的,删掉它:
还有长度为6的,完全可以删除,但是删除之后,边的数量就是
n
−
1
n-1
n−1了,满足树的条件,不能继续删除了:
Python实现
原理清楚之后,代码就任意写了,以下是python代码:
# _*_ coding:utf-8
import functools
class Edge:
def __init__(self, to, weight):
self.__to = to
self.__weight = weight
@property
def to(self):
return self.__to
@property
def weight(self):
return self.__weight
def __str__(self):
return f'->{self.__to}(w={self.__weight})'
def __repr__(self):
return f'->{self.__to}(w={self.__weight})'
# wu
class WeightedGraph:
def __init__(self, vertices, edges, pos):
self.__vertices = vertices
self.__edges = edges
# 用于图形化
self.__pos = pos
@property
def vertices(self):
return self.__vertices
@property
def edges(self):
return self.__edges
def reverse_delete(self):
all_edges = []
for i, edges in enumerate(self.__edges):
for edge in edges:
if i > edge.to:
all_edges.append((i, edge))
all_edges.sort(key=functools.cmp_to_key(lambda x, y: x[1].weight - y[1].weight))
# 拷贝一套边
a = [edges[:] for edges in self.__edges]
x = WeightedGraph(self.__vertices, a, pos=self.__pos)
n = len(self.__vertices)
while x.is_connected() and len(all_edges) > n - 2:
edge = all_edges.pop()
# 双向删除边
f = edge[0]
t = edge[1].to
if edge[1] in a[f]:
a[f].remove(edge[1])
reversed_edge = None
for i, e in enumerate(a[t]):
if e.to == f:
a[t].pop(i)
reversed_edge = e
break
if not x.is_connected():
# 加回去
a[f].append(edge[1])
a[t].append(reversed_edge)
else:
print(len(all_edges), x.to_dot())
return a
WHITE = 0
GRAY = 1
BLACK = 2
def is_connected(self):
colors = [WeightedGraph.WHITE for _ in self.__vertices]
colors[0] = WeightedGraph.GRAY
stack = [0]
count = 0
while len(stack) > 0:
u = stack.pop()
count += 1
for v in self.__edges[u]:
if colors[v.to] == WeightedGraph.WHITE:
stack.append(v.to)
colors[v.to] = WeightedGraph.GRAY
colors[u] = WeightedGraph.BLACK
return count == len(self.__vertices)
def to_dot(self):
pos = self.__pos
dot_s = 'graph s {\n\tlayout=fdp\n'
for i, v in enumerate(self.__vertices):
dot_s += f'\t"{v}"[pos="{pos[i]}"];\n'
for i, e in enumerate(self.__edges):
for t in e:
if t.to < i:
continue
dot_s += f'\t\"{self.__vertices[i]}\"--"{self.__vertices[t.to]}"[label="{t.weight}"];\n'
dot_s += '}\n'
return dot_s
Python测试代码
import unittest
from com.youngthing.graph.reverse_delete import Edge, WeightedGraph
class ReverseDeleteTestCase(unittest.TestCase):
def test_reverse_delete(self):
# 做一张图
vertices = ['A', 'B', 'C', 'D', 'E', 'F', 'G', ]
# 设计容量
edges = [
[Edge(1, 4), Edge(2, 8), ], # a
[Edge(0, 4), Edge(2, 9), Edge(3, 8), Edge(4, 10)], # B
[Edge(0, 8), Edge(1, 9), Edge(3, 2), Edge(5, 1)], # C
[Edge(1, 8), Edge(2, 2), Edge(4, 7), Edge(5, 9)], # D
[Edge(1, 10), Edge(3, 7), Edge(5, 5), Edge(6, 6)], # E
[Edge(2, 1), Edge(3, 9), Edge(4, 5), Edge(6, 2)], # f
[Edge(4, 6), Edge(5, 2)], # g
]
pos = ["0,0!", "2,1!", "2,-1!", "4,0!", "6,1!", "6,-1!", "8,0!", ]
fn = WeightedGraph(vertices, edges, pos)
print(fn.to_dot())
tree = fn.reverse_delete()
print(WeightedGraph(vertices, tree, pos).to_dot())
if __name__ == '__main__':
unittest.main()