本次题目来自于卡码网
prim算法
prim算法:不断加入最近节点并更新最近距离,得到最小生成树
if __name__ == '__main__':
v, e = map(int, input().strip().split())
# 填一个默认最大值,题目描述val最大为10000
grid = [[10001] * (v + 1) for _ in range(v + 1)]
for i in range(e):
x, y, k = map(int, input().strip().split())
# 因为是双向图,所以两个方向都要填上
grid[x][y] = k
grid[y][x] = k
# 所有节点到最小生成树的最小距离
minDist = [10001] * (v + 1)
# 这个节点是否在树里
isInTree = [False] * (v + 1)
# 我们只需要循环 n-1次,建立 n - 1条边,就可以把n个节点的图连在一起
for i in range(1, v):
# prim三部曲,第一步:选距离生成树最近节点
cur = -1 # 选中哪个节点 加入最小生成树
minVal = float('inf')
for j in range(1, v + 1): # 1 - v,顶点编号,这里下标从1开始
# 选取最小生成树节点的条件:
# (1)不在最小生成树里
# (2)距离最小生成树最近的节点
if not isInTree[j] and minDist[j] < minVal:
minVal = minDist[j]
cur = j
# 2、prim三部曲,第二步:最近节点(cur)加入生成树
isInTree[cur] = True
# 3、prim三部曲,第三步:更新非生成树节点到生成树的距离(即更新minDist数组)
# cur节点加入之后, 最小生成树加入了新的节点,那么所有节点到 最小生成树的距离(即minDist数组)需要更新一下
# 由于cur节点是新加入到最小生成树,那么只需要关心与 cur 相连的 非生成树节点 的距离 是否比 原来 非生成树节点到生成树节点的距离更小了呢
for j in range(1, v + 1):
# 更新的条件:
# (1)节点是 非生成树里的节点
# (2)与cur相连的某节点的权值 比 该某节点距离最小生成树的距离小
# 很多录友看到自己 就想不明白什么意思,其实就是 cur 是新加入 最小生成树的节点,那么 所有非生成树的节点距离生成树节点的最近距离 由于 cur的新加入,需要更新一下数据了
if not isInTree[j] and grid[cur][j] < minDist[j]:
minDist[j] = grid[cur][j]
# 统计结果
result = 0
for i in range(2, v + 1): # 不计第一个顶点,因为统计的是边的权值,v个节点有 v-1条边
result += minDist[i]
print(result)
kruskal算法
kruskal算法:对所有的边权重排序,按照从低到高;将最小权重的边加入到图中,如果已经在一个集合中了,则舍弃
class UnionFind:
def __init__(self, n):
self.n = n
self.father = [0] * (n + 1)
for i in range(n + 1):
self.father[i] = i
def find(self, u):
if self.father[u] == u:
return u
else:
self.father[u] = self.find(self.father[u])
return self.father[u]
def join(self, u, v):
u = self.find(u)
v = self.find(v)
if u == v:
return
self.father[v] = u
if __name__ == '__main__':
v, e = map(int, input().strip().split())
edges = []
result_val = 0
for _ in range(e):
v1, v2, val = map(int, input().strip().split())
edges.append((v1, v2, val))
# 执行Kruskal算法
# 按边的权值对边进行从小到大排序
edges.sort(key=lambda x: x[2])
# 并查集初始化
union_find = UnionFind(v)
# 从头开始遍历边
for edge in edges:
# 并查集,搜出两个节点的祖先
x = union_find.find(edge[0])
y = union_find.find(edge[1])
# 如果祖先不同,则不在同一个集合
if x != y:
result_val += edge[2] # 这条边可以作为生成树的边
union_find.join(x, y) # 两个节点加入到同一个集合
print(result_val)
总结
在节点数量固定的情况下,图中的边越少,Kruskal 需要遍历的边也就越少。
而 prim 算法是对节点进行操作的,节点数量越少,prim算法效率就越优。
所以在 稀疏图中,用Kruskal更优。 在稠密图中,用prim算法更优。
复杂度
- Prim 算法 时间复杂度为 O(n^2),其中 n 为节点数量,它的运行效率和图中边树无关,适用稠密图。
- Kruskal算法 时间复杂度 为 nlogn,其中n 为边的数量,适用稀疏图