克鲁斯卡尔算法
克鲁斯卡尔算法(kruskal)跟普里姆算法一样,目的都是求无向图的最小生成树。普里姆算法核心在于一个顶点接一个顶点的找出最短路径,克鲁斯卡算法在于将每一条边进行升序排序,然后通过边进行筛选从而组成最小生成树。
实现步骤
核心思想在于按权值从小到大排序选择n-1条边,并保证选择边不会构成回路。用到核心的数据结构并查集来判断是否构成回路。
- 将所有的边按权值大小升序排列
- 创建一个数组selectEdges存在选择出来的边
- 循环遍历已经排好序的边,如果该边不构成回路,则添加到selectEdges
并查集
并查集是一种树型或者链表的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
- 初始化数组为的值为索引本身,如果arr[n]=n 说明该点的根节点就是他本身
- 并查集核心方法找到一个节点的根节点find 和 union合并2个节点
- union合并方法的时候如果根节点不同,则将一个根节点挂在另一个根节点上集合
from functools import reduce
class Edge(object):
"""
边 start起点 end终点 weight 权值
"""
def __init__(self, start, end, weight):
self.start = start
self.end = end
self.weight = weight
def __str__(self):
return '%s->%s weight=%d' % (self.start, self.end, self.weight)
class MiniTree(object):
def __init__(self, vertex, weight):
"""
最小生成树
"""
self.vertex = vertex
self.weight = weight
def get_all_edges(self):
all_edges = []
for s in range(len(self.vertex)):
for e in range(len(self.vertex)):
if self.weight[s][e] != 10000:
all_edges.append(Edge(s, e, self.weight[s][e]))
return all_edges
def disjoin_set_find_root(self, parent_root, start):
"""
使用并查集思想找到一个顶点的终点
:return:
"""
while parent_root[start] != start:
return self.disjoin_set_find_root(parent_root, parent_root[start])
return start
def disjoin_set_union(self, parent_root, start, end):
"""
合并
:param parent_root:
:param start:
:param end:
:return:
"""
start_root = self.disjoin_set_find_root(parent_root, start)
end_root = self.disjoin_set_find_root(parent_root, end)
if start_root != end_root:
parent_root[start_root] = end_root
def create_min_tree_by_kruskal(self):
"""
1. 将所有的边按权值大小升序排列
2. 创建一个数组selectEdges存在选择出来的边
3. 循环遍历已经排好序的边,如果该边不构成回路,则添加到selectEdges
:return:
"""
# 所有选择的边 并升序排列
all_edges = self.get_all_edges()
all_edges.sort(key=lambda edge: edge.weight)
select_edges = []
parent_root = [i for i in range(len(self.vertex))]
while len(select_edges) < len(self.vertex) - 1:
# 判断根已经选择了的边是否构成回路 使用到了并查集思想
for edge in all_edges:
start = edge.start
end = edge.end
# 核心判断是不是构成回路 不构成回路 加入选择边和
if self.disjoin_set_find_root(parent_root, start) != self.disjoin_set_find_root(parent_root, end):
select_edges.append(edge)
all_edges.remove(edge)
self.disjoin_set_union(parent_root, start, end)
return select_edges
if __name__ == '__main__':
mini_tree = MiniTree(['A', 'B', 'C', 'D', 'E', 'F', 'G'],
[[10000, 5, 7, 10000, 10000, 10000, 2], [5, 10000, 10000, 9, 10000, 10000, 3],
[7, 10000, 10000, 10000, 8, 10000, 10000], [10000, 9, 10000, 10000, 10000, 4, 10000],
[10000, 10000, 8, 10000, 10000, 5, 4], [10000, 10000, 10000, 4, 5, 10000, 6],
[2, 3, 10000, 10000, 4, 6, 10000], ])
min_tree_by_kruskal = mini_tree.create_min_tree_by_kruskal()
print(reduce(lambda x, y: x + y, map(lambda x: x.weight, min_tree_by_kruskal)))
for i in min_tree_by_kruskal:
print('%s->%s weight=%d' % (mini_tree.vertex[i.start], mini_tree.vertex[i.end], i.weight))