从我读到的关于Kruskal算法的内容来看,似乎我们必须使用union find数据结构。但是为什么我不能只使用一个布尔数组来指示是否已经将特定顶点添加到MST中并且只是检查边缘的两个顶点是否都没有添加到MST中?即像这样的东西(这是类似Java的,但是伪代码,所以它可能无法编译):
public Queue<Edge> getMST(Graph graph) {
boolean[] visited = new boolean[graph.numVertices()];
PriorityQueue<Edge> pq = new PriorityQueue<>();
Queue<Edge> mstPath = new Queue<>();
pq.addAll(graph.edges());
while(mstPath.size() < graph.numVertices() - 1 && pq.isNotEmpty()) {
Edge curr = pq.getMin();
if (visited[curr.to] && visited[curr.from]) continue; // ignore because both vertices of this edge are in MST
mstPath.add(curr);
visited[curr.to] = true;
visited[curr.from] = true;
}
return mstPath;
}
那我为什么要使用Union Find数据结构呢?上述工作不会正确吗?
答案 0 :(得分:2)
Kruskal算法的工作方式是从一堆断开的顶点开始,一次添加一个边,直到你有一个连接所有顶点的生成树。如果在每一步添加最便宜的边缘,最终的生成树将具有最低成本。
所以你在每一步所做的就是找到你可以在不形成循环的情况下添加的最便宜的边缘。要做到这一点,您需要快速确定是否已连接一对顶点。如果它们已经连接,则在它们之间添加边缘将引入一个循环。如果它们未连接,您可以添加边,这将连接图中的两个单独组件。
这种天真的数据结构是有一个数组,为每个顶点存储一个数字,每个不同的数字对应一个不同的连接组件。 (使用布尔值是不够的 - 在开始时,每个顶点都有一个组件)。此数组允许您检查两个顶点是否在恒定时间内位于同一组件中,但合并两个组件是O(N),因为您需要更新其所有元素。联合查找数据结构更加聪明,可以非常快速地完成这两项操作。
顺便说一下,也许你把Kruskal的算法与Prim的算法混淆了。在Prim的算法中,您可以跟踪单个树,并在每个步骤中将树中尚未存在的一个顶点添加到树中。
但即便如此,你的建议也不会有效,因为无论何时向树中添加边,其中一个顶点在树中,另一个顶点不在树中。