克鲁斯卡尔算法(Kruskal):
设有一个有n个顶点的连通网N={V,E},最初先构造一个只有n个顶点,没有边的非连通图T={V, E},图中每个顶点自成一个连通分量。当在E中选到一条具有最小权值的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则将此边舍去,重新选择一条权值最小的边。如此重复下去,直到所有顶点在同一个连通分量上为止。
普里姆算法的基本思想:
普里姆算法是另一种构造最小生成树的算法,它是按逐个将顶点连通的方式来构造最小生成树的。
从连通网络 N = { V, E }中的某一顶点 u0 出发,选择与它关联的具有最小权值的边(u0, v),将其顶点加入到生成树的顶点集合U中。以后每一步从一个顶点在U中,而另一个顶点不在U中的各条边中选择权值最小的边(u, v),把该边加入到生成树的边集TE中,把它的顶点加入到集合U中。如此重复执行,直到网络中的所有顶点都加入到生成树顶点集合U中为止。
假设G=(V,E)是一个具有n个顶点的带权无向连通图,T(U,TE)是G的最小生成树,其中U是T的顶点集,TE是T的边集,则构造G的最小生成树T的步骤如下:
(1)初始状态,TE为空,U={v0},v0∈V;
(2)在所有u∈U,v∈V-U的边(u,v) ∈E中找一条代价最小的边(u′,v′)并入TE,同时将v′并入U;
重复执行步骤(2)n-1次,直到U=V为止。
克鲁斯卡尔算法和普里姆算法不同的是其寻找顺序不同,克鲁斯卡尔算法是先从边开始找,普里姆算法是先从点开始寻找,二者都是寻找图中最小生成树的方式。
下面用图来说明两种算法:
克鲁斯卡尔算法寻找最小生成树过程:
普里姆算法寻找最小生成树过程:
由此我们可以看出,两种算法得到的最小生成树是相同的,仅仅是过程不一样而已。
下面我们来通过代码实现:
克鲁斯卡尔算法实现过程:
那么我们如何判断生成树中没有环产生呢?
判断方式是这样的,判断要加入的该边的终点否在生成树中,如果没在生成树中,那么就可以加入,如果在生成树中,那么就不能加入。例如:
比如上图中如果生成树中已经包含了AB边,BC边,如果要加入AC边的话,那一定会构成环,为什么呢,那是因为加入AC边时,从该边的起点A出发,在已经构建好的生成树中已经有从A到C点的边A-B-C,那在加入AC边时就会产生环,所以我们的判断条件就是判断加入改边时,已经构建好的生成树中是否和该边有共同的终点,如果有,那么就不能加入改边,直到生成树中存在n-1条边时终止。
下面代码进行实现:
import sys
class GMap(object):
def __init__(self, vertex_data: [], matrix: []): # 传入顶点数组
self.edge_num = 0 # 边的个数
# self.vertex_data = vertex * [0] # 顶点数组
self.vertex_data = vertex_data
# self.matrix = [[0 for row in range(len(vertex_data))] for col in range(len(vertex_data))] # 邻接矩阵
self.matrix = matrix
# 使用 INF 表示两个顶点不能连通
self.inf = float('inf') # 获取浮点型最大值
# self.inf = sys.maxsize # 获取整形最大值
# 统计边的条数
for i in range(len(vertex_data)):
for j in range(i + 1, len(vertex_data)):
if self.matrix[i][j] != self.inf:
self.edge_num += 1
# print(self.edge_num)
def print_test(self): # 打印测试
for i in range(len(self.vertex_data)):
for j in range(len(self.vertex_data)):
print(self.matrix[i][j], end=' ')
print()
# 对边进行排序处理,这里使用一下冒泡排序,其他排序方式都可以
def sort_edges(self, edges: []):
for i in range(len(edges) - 1):
flag = False
for j in range(len(edges) - 1 - i):
if edges[j].weight > edges[j + 1].weight:
edges[j], edges[j + 1] = edges[j + 1], edges[j]
flag = True
if not flag:
break
# 查找传入顶点的位置
def get_position(self, ver_data: str):
"""
:param ver_data: 传入顶点的值,例如:"A""B"
:return: 找到返回ch顶点对应的下标,如果没找到,返回-1
"""
for i in range(len(self