Kruskal 알고리즘과 마찬가지로 Prim 알고리즘도 최소 신장 트리 알고리즘이지만 Kruskal 알고리즘과는 상당히 다릅니다.
Prim의 알고리즘은 전체적으로 "잠금 해제" + "선택", point -> edge -> point -> edge 입니다 .
최소 스패닝 트리이기 때문에 무방향 그래프도 겨냥하고 있기 때문에 마음대로 한 점을 진입점으로 선택할 수 있고, 이 점을 잠금 해제하면 이 점에서 나오는 모든 가장자리를 얻고 다른 쪽도 잠금 해제할 수 있습니다. 이 모서리 중 가중치가 가장 작은 모서리를 통과합니다. 그래서 반복적으로. 최소 스패닝 트리가 형성될 때까지.
그림에 표시된 대로
왼쪽은 원본 그림으로, a 지점(a에서 나온 것으로 가정하면 어떤 지점이라도 괜찮음)에서 시작하여 a 지점을 잠금 해제(잠금 해제된 지점이 원을 그립니다)하고 직접 시작하는 가중치를 잠금 해제합니다. a점부터 1, 2, 9의 세 변(변이 점선으로 잠금 해제됨), 가중치에 따라 1의 변이 선택됩니다(색상을 변경하려면 특정 변을 선택). 그리고 포인트 b를 잠금해제합니다.
잠금 해제된 지점 b를 통해 가중치가 1, 3, 4, 9인 간선을 잠금 해제할 수 있으며, 이때 bd 간선의 가중치는 1 이상이므로 d 지점은 잠금 해제됩니다.
d를 잠금 해제한 후, d가 바로 나오는 4면도 잠금 해제됩니다. 가중치가 작은 쪽을 다시 2로 선택하는데, 이때 d는 이미 해제되어 있으므로 2는 고려하지 말고, be가 3인 쪽을 선택해 다시 잠금을 해제한다.
이때 잠금 해제 후 그래프는 위와 같으며, 가중치 6과 7이 있는 변은 e점을 잠금 해제한 후 잠금이 해제됩니다.
이때 모든 Edge의 잠금이 해제되어 있으므로 가중치가 작은 Edge와 링을 형성하지 않는 점을 선택하여 잠금을 해제합니다.
마지막으로 선택되지 않은 모든 가장자리가 제거되고 나머지는 최소 스패닝 트리입니다.
요약하다
- 최소 스패닝 트리는 도달 가능한 모든 지점을 최소 거리로 연결하는 트리입니다.
- 따라서 무작위로 각 점에 대해 전체 Edge 중에서 가중치가 가장 작은 Edge를 선택하여 이 점을 얻으면 조직은 확실히 최소 스패닝 트리로 구성된 답이 될 것입니다.
코드 구현
위의 그림을 기반으로 코드 구현이 이루어집니다. 포인트 > 엣지 -> 포인트 -> 엣지를 잠금해제합니다.
가장 바깥쪽 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;
}