给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1 ,同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti] 表示在 fromi 和 toi 节点之间有一条带权无向边。最小生成树 (MST) 是给定图中边的一个子集,它连接了所有节点且没有环,而且这些边的权值和最小。
请你找到给定图中最小生成树的所有关键边和伪关键边。如果从图中删去某条边,会导致最小生成树的权值和增加,那么我们就说它是一条关键边。伪关键边则是可能会出现在某些最小生成树中但不会出现在所有最小生成树中的边。
请注意,你可以分别以任意顺序返回关键边的下标和伪关键边的下标。
示例 1:
输入:n = 5, edges = [[0,1,1],[1,2,1],[2,3,2],[0,3,2],[0,4,3],[3,4,3],[1,4,6]]
输出:[[0,1],[2,3,4,5]]
解释:上图描述了给定图。
下图是所有的最小生成树。注意到第 0 条边和第 1 条边出现在了所有最小生成树中,所以它们是关键边,我们将这两个下标作为输出的第一个列表。
边 2,3,4 和 5 是所有 MST 的剩余边,所以它们是伪关键边。我们将它们作为输出的第二个列表。
示例 2 :输入:n = 4, edges = [[0,1,1],[1,2,1],[2,3,1],[0,3,1]]
输出:[[],[0,1,2,3]]
解释:可以观察到 4 条边都有相同的权值,任选它们中的 3 条可以形成一棵 MST 。所以 4 条边都是伪关键边。
提示:
2 <= n <= 100
1 <= edges.length <= min(200, n * (n - 1) / 2)
edges[i].length == 3
0 <= fromi < toi < n
1 <= weighti <= 1000
所有 (fromi, toi) 数对都是互不相同的。来源:力扣(LeetCode)
链接:题目链接题解:Python解法的kruskal算法
class Solution:
def __init__(self):self.MAX_VALUE = 1e6
def find(self, prev, node): # 并查集模板
if prev[node] != node:prev[node] = self.find(prev, prev[node]) # 路径压缩
return prev[node]
def kruskal(self, edges, n, k, remove=True): # 克鲁斯卡尔算法找最小生成树
prev = [i for i in range(n)]
count, cost = 0, 0
if not remove: # 必包含第k条边
prev[self.backup[k][1]] = self.backup[k][0]
cost = self.backup[k][2]
count = 1
for i, edge in enumerate(edges):
if remove and k >= 0 and edge == self.backup[k]:continue # 特殊情况不包含
p1, p2 = self.find(prev, edge[0]), self.find(prev, edge[1])
if p1 == p2: continue # 同一棵树直接跳过
prev[p2] = p1 # 两树合并
cost += edge[2]
count += 1
if count == n - 1:return cost
return self.MAX_VALUE
def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]:
self.backup = [edge for edge in edges] # 保持顺序 不用深拷贝
edges.sort(key=lambda x : x[2]) # 升序排序
minCost = self.kruskal(edges, n, -1)
result = [[], []]
for i in range(len(edges)):
if self.kruskal(edges, n, i) > minCost:result[0].append(i) # 关键边
elif self.kruskal(edges, n, i, False) == minCost: result[1].append(i) # 伪关键边
return result