Kruskal アルゴリズムと同様、Prim アルゴリズムも最小スパニング ツリー アルゴリズムですが、Kruskal アルゴリズムとは大きく異なります。
Prim の全体的なアルゴリズムは、「ロック解除」+「選択」、ポイント -> エッジ -> ポイント -> エッジ です。
これは最小スパニング ツリーであるため、無向グラフも対象としており、任意のポイントをエントリ ポイントとして選択できます。このポイントのロックを解除すると、このポイントからすべてのエッジを取得し、他のエッジのロックを解除できます。これらのエッジの中で最小の重みを持つエッジを通過します。何度もそう。最小スパニングツリーが形成されるまで。
図のように:
左側が元の図で、点aから開始(aからだと仮定して任意の点で問題ありません)、点aのロックを解除(ロック解除された点は円を描きます)、直接開始の重みのロックを解除します点 a から 1、2 、9 の 3 つの辺 (辺は点線でロック解除されます)、および 1 の辺が重みに応じて選択されます (特定の辺を選択すると色が変わります)。そしてポイントbのロックを解除します。
ロックが解除された点 b を通じて、重み 1、3、4、および 9 を持つエッジのロックが解除されますが、このとき、bd エッジの重みは少なくとも 1 であるため、点 d はロックが解除されます。
d解放後はdが直接出てくるサイド4も解放されます。再度ウェイトの小さい方を2として選択しますが、この時点では既にdがアンロックされているので2は考慮せず、beが3となる方を選択して再度アンロックします。
このとき、ロック解除後のグラフは上図のようになり、点eのロックが解除された後にウェイト6と7の辺がロック解除されます。
この時点で全てのエッジのロックが解除されていますので、ウェイトが小さいエッジとリングにならない点を選択してロックを解除します。
最後に、選択されていないエッジがすべて削除され、残りが最小スパニング ツリーになります。
要約する
- 最小スパニング ツリーは、到達可能なすべてのポイントを最小距離で接続します。
- したがって、各点についてランダムにすべての辺の中から最も重みの小さい辺を選択してその点を求めれば、その組織は必ず最小全域木から構成される答えとなる。
コードの実装
上の図に基づいてコードを実装します。ポイント > エッジ -> ポイント -> エッジでロックを解除します。
最も外側の for ループは「フォレスト」を防ぐことができます。a -> bc ->d e->f、a は b を見つけることができ、c は d を見つけることができ、e は f を見つけることができます。しかし、エース同士は何の関係もありません。
public static class EdgeComparator implements Comparator<Edge> {
@Override
public int compare(Edge o1, Edge o2) {
return o1.weight - o2.weight;
}
}
public static Set<Edge> primMST(Graph graph) {
//放入PriorityQueue中,并根据边的权重进行排序
PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
//解锁的点
Set<Node> setNodes = new HashSet<>();
//构成最小生成树的所有边
Set<Edge> result = new HashSet<>();
//遍历图集中所有的点
for (Node node : graph.nodes.values()) {
//如果没解锁
if (!setNodes.contains(node)) {
setNodes.add(node);
//将点的所有的边,放到PriorityQueue中排序
for (Edge edge : node.edges) {
priorityQueue.add(edge);
}
while (!priorityQueue.isEmpty()) {
Edge edge = priorityQueue.poll();
//获取到这个边连接的to点
Node toNode = edge.to;
if (!setNodes.contains(edge.to)) {
//解锁to点
setNodes.add(toNode);
result.add(edge);
//并且将to点所有的边也都放到Queue中
for (Edge nextEdge : toNode.edges) {
priorityQueue.add(nextEdge);
}
}
}
}
//如果防森林,就不break
break;
}
return result;
}