引入
加权无向图中的边我们就不能简单向之前一样使用v-w两个顶点表示了(之前的adj_list列表中存放的是顶点,而必须要给边关联一个权重值,因此我们可以使用对象来描述一条边(现在要存放边对象Edge)。
class Edge:
def __init__(self, vertex1, vertex2, weight):
self.vertex1 = vertex1
self.vertex2 = vertex2
self.weight = weight
def get_weight(self):
return self.weight
def either(self):
"""Return either vertex of this Edge"""
return self.vertex1
def opposite(self, v):
return self.vertex1 if v == self.vertex2 else self.vertex2
def compare(self, edge):
return 1 if self.weight > edge.weight else (-1 if self.weight < edge.weight else 0)
代码较简单,测试就略过了,这里实现的Edge对象将用于后面的类方法实现。
Prim算法跳转
Kruskal算法跳转
from Structure.graph.Edge import Edge
class WeightedUndigraph:
def __init__(self, v):
self.num_vertices = v
self.num_edges = 0
self.adj_list = [[] for _ in range(v)]
def get_num_vertices(self):
return self.num_vertices
def get_num_edges(self):
return self.num_edges
def add_edge(self, edge: Edge):
v1 = edge.either()
v2 = edge.opposite(v1)
self.adj_list[v1].append(edge)
self.adj_list[v2].append(edge)
self.num_edges += 1
def adjacent_edges(self, vertex):
return self.adj_list[vertex]
def all_edges(self):
all_edges = []
for i in range(self.num_vertices):
for edge in self.adj_list[i]:
if edge.opposite(i) < i:
all_edges.append(edge)
return all_edges
引入
举例
最小生成树的定义
约定
最小生成树的性质
在实现寻找最小生成树之前,我们先来了解一下实现寻找最小生成树的两个原理
切分定理
先了解概念
要从一副连通图中找出该图的最小生成树,需要通过切分定理完成。
再了解定义
切分定理:
定义
计算图的最小生成树的算法有很多种,但这些算法都可以看做是贪心算法的一种特殊情况,这些算法的不同之处在于保存切分和判定权重最小的横切边的方式。
prim算法定义
Prim算法的切分规则:
在实现prim算法寻找MST之前,我们要借助一个最小索引优先队列,来方便地取出最小权重边:
索引最小有限队列代码传送门
加权无向边,up↑
加权无向图,up↑
from Structure.graph.WeightedUndigraph import WeightedUndigraph
from Structure.graph.Edge import Edge
from Structure.PriorityQueue.IndexMinpriorutyQueue import IndexMinPriorityQueue
from math import inf
class PrimMST:
def __init__(self, graph):
"""MST here represent the Minimum Spanning Tree of the current loop"""
self.graph = graph
# Memorize the cheapest edge to MST of each vertex(index)
self.min_edge_to_MST = [None for _ in range(self.graph.get_num_vertices())]
# Store the smallest weight of each vertex(index)'s edge to MST;
# Initialize it with infinite plus, we will compare out a minimum weight after
self.min_weight_to_MST = [+inf for _ in range(self.graph.get_num_vertices())]
# Mark a True if a vertex(index) has been visited
self.marked = [False for _ in range(self.graph.get_num_vertices())]
# Memorize the smaller weight of each vertex(index)'s edge connected to MST
self.the_cut_edges = IndexMinPriorityQueue(self.graph.get_num_vertices())
# Initialize a 0.0 as the minimum weight to weight_to_MST
self.min_weight_to_MST[0] = 0.0
self.the_cut_edges.insert(0, 0.0)
while not self.the_cut_edges.is_empty():
# Take out the minimum-weighted vertex, and make a visit(update) for it
self.visit(self.the_cut_edges.delete_min_and_get_index())
def visit(self, v):
"""Update the MST"""
self.marked[v] = True
for e in self.graph.adjacent_edges(v):
w = e.opposite(v)
# Check if the opposite vertex of v in edge e is marked, if did, skip this loop
if self.marked[w]:
continue
# Find out the minimum-weighted-edge vertex opposite to this vertex(v)
if e.get_weight() < self.min_weight_to_MST[w]: # e.get_weight():Get weight of the edge between v and w
# Update the minimum edge and weight
# print(f"v: {v}, w: {w}, min_weight_edge: {e.get_weight()}")
self.min_edge_to_MST[w] = e
self.min_weight_to_MST[w] = e.get_weight()
if self.the_cut_edges.is_index_exist(w):
# print(w, e.get_weight())
self.the_cut_edges.change_item(w, e.get_weight())
else:
self.the_cut_edges.insert(w, e.get_weight())
def min_weight_edges(self):
return [edge for edge in self.min_edge_to_MST if edge]
if __name__ == '__main__':
with open('../MST.txt', 'r') as f:
num_vertices = int(f.readline())
num_edges = int(f.readline())
graph = WeightedUndigraph(num_vertices)
for e in range(num_edges):
v1, v2, w = f.readline().split()
graph.add_edge(Edge(int(v1), int(v2), float(w)))
P_MST = PrimMST(graph)
for e in P_MST.min_weight_edges():
v = e.either()
w = e.opposite(v)
weight = e.weight
print(f"v: {v} w: {w} weight: {weight}")
运行结果
v: 1 w: 7 weight: 0.19
v: 0 w: 2 weight: 0.26
v: 2 w: 3 weight: 0.17
v: 4 w: 5 weight: 0.35
v: 5 w: 7 weight: 0.28
v: 6 w: 2 weight: 0.4
v: 0 w: 7 weight: 0.16
即是最小生成树的所有边的权重和
MST.txt
8
16
4 5 0.35
4 7 0.37
5 7 0.28
0 7 0.16
1 5 0.32
0 4 0.38
2 3 0.17
1 7 0.19
0 2 0.26
1 2 0.36
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93
定义
kruskal算法和prim算法的区别:
Kruskal算法在寻找MST时,需要用到并查集来实现,当寻找的树不是连通的时候(初始时所有结点都是分隔开的),它就会为在每一颗树中搜寻出一个MST。
并查集传送、
最小优先队列传送(最小优先队列需要小小地修改比较的方法,应该比较传入的Edge对象的weight,否则直接比较会报错):
def less(self, i, j):
return operator.lt(self.heap[i].weight, self.heap[j].weight)
优化后的并查集传送
最小优先队列传送
加权无向边
加权无向图
from Structure.UF.UF_Tree_Weighted import UF_Tree_Weighted
from Structure.PriorityQueue.MinPriorityQueue import MinPriorityQueue
from Structure.graph.WeightedUndigraph import WeightedUndigraph
from Structure.graph.Edge import Edge
class KruskalMST:
def __init__(self, graph):
self.graph = graph
self.N = self.graph.num_vertices
self.UFT = UF_Tree_Weighted(self.N)
self.all_edges = MinPriorityQueue()
self.edges_MST = []
for e in self.graph.get_all_edges():
self.all_edges.append(e)
while not self.all_edges.is_empty() and len(self.edges_MST) < self.N - 1:
min_edge = self.all_edges.extract_min()
v = min_edge.either()
w = min_edge.opposite(v)
if self.UFT.in_the_same_group(v, w):
continue
self.UFT.unite(v, w)
self.edges_MST.append(min_edge)
def get_all_edges_mst(self):
return self.edges_MST
if __name__ == '__main__':
with open('../MST.txt', 'r') as f:
num_vertices = int(f.readline())
num_edges = int(f.readline())
graph = WeightedUndigraph(num_vertices)
for e in range(num_edges):
v1, v2, w = f.readline().split()
graph.add_edge(Edge(int(v1), int(v2), float(w)))
K_MST = KruskalMST(graph)
for e in K_MST.get_all_edges_mst():
v = e.either()
w = e.opposite(v)
weight = e.weight
print(f"v: {v} w: {w} weight: {weight}")
运行结果:
v: 0 w: 7 weight: 0.16
v: 2 w: 3 weight: 0.17
v: 1 w: 7 weight: 0.19
v: 0 w: 2 weight: 0.26
v: 5 w: 7 weight: 0.28
v: 4 w: 5 weight: 0.35
v: 6 w: 2 weight: 0.4
MST.txt
8
16
4 5 0.35
4 7 0.37
5 7 0.28
0 7 0.16
1 5 0.32
0 4 0.38
2 3 0.17
1 7 0.19
0 2 0.26
1 2 0.36
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93