java - 在 Java 中实现 Prim 的 MST
问题描述
我无法让我的代码遵循第 635 页中 CLRS 的最小生成树 (MST) 示例。我还在字面上实现 CLRS 的 MST-Prim 伪代码,即
所以我实际上有两个问题。第一个是在将节点添加b
到 MST 之后,我的代码选择作为 PriorityQueue 节点中的下一个元素,h
而不是 node c
。我不确定 Java 的实现如何在出现关系时选择元素,所以如果有什么我可以做的,请告知。为了暂时规避这个问题,我将边缘修改a-h
为权重为 9。
然后一切都运行良好,直到它选择节点d
而不是g
添加f
到 MST 之后,我觉得这很奇怪,因为 PriorityQueue 显然g
具有key=6
.
我的实现如下:
import java.util.LinkedList;
import java.util.PriorityQueue;
class Edge{
private Node source;
private Node destination;
private int weight;
public void setSource(Node source) {
this.source = source;
}
public void setDestination(Node destination) {
this.destination = destination;
}
public void setWeight(int weight) {
this.weight = weight;
}
public int getWeight() {
return this.weight;
}
public Node getSource() {
return this.source;
}
public Node getDestination() {
return this.destination;
}
}
class Node implements Comparable<Node>{
private String label;
private LinkedList<Edge> edges;
private int key;
private Node daddy;
Node() {
this.edges = new LinkedList();
}
public void setLabel(String label) {
this.label = label;
}
public void setKey(int key) {
this.key = key;
}
public void setDaddy(Node daddy) {
this.daddy = daddy;
}
public String getLabel() {
return this.label;
}
public int getKey() { return this.key; }
public LinkedList getEdges() {
return this.edges;
}
@Override
public int compareTo(Node o) {
if (this.getKey() > o.getKey()) {
return 1;
}
else if (this.getKey() < o.getKey()) {
return -1;
}
else {
return 0;
}
}
}
public class Graph {
private int numberOfNodes;
private Node[] weightedGraph;
public Graph(int graphV) {
this.numberOfNodes = graphV;
this.weightedGraph = new Node[this.numberOfNodes];
for (int i = 0; i < this.numberOfNodes; i++) {
this.weightedGraph[i] = new Node();
}
}
public void addEdge(String sourceLabel, String destinationLabel, int weight) {
Node sourceNode = null;
Node destinationNode = null;
for (Node node: this.weightedGraph) {
if (node.getLabel().contains(sourceLabel)) {
sourceNode = node;
}
if (node.getLabel().contains(destinationLabel)) {
destinationNode = node;
}
}
Edge e = new Edge();
e.setWeight(weight);
e.setSource(sourceNode);
e.setDestination(destinationNode);
sourceNode.getEdges().add(e);
}
public void minimumSpanningTree(String root) {
Node rootNode = null;
for (Node vertex: this.weightedGraph) {
vertex.setKey(Integer.MAX_VALUE);
vertex.setDaddy(null);
if (vertex.getLabel().contains(root)) {
rootNode = vertex;
}
}
rootNode.setKey(0);
PriorityQueue<Node> nodePriorityQueue = new PriorityQueue<>();
for (Node vertex: this.weightedGraph) {
nodePriorityQueue.add(vertex);
}
int min = 0;
while (!nodePriorityQueue.isEmpty()) {
Node u = nodePriorityQueue.peek();
LinkedList<Edge> uEdges= u.getEdges();
for (Edge e: uEdges) {
Node v = e.getDestination();
int u_vWeight = e.getWeight();
if (nodePriorityQueue.contains(e.getDestination()) && u_vWeight < v.getKey()) {
v.setDaddy(u);
v.setKey(u_vWeight);
}
}
nodePriorityQueue.remove(u);
min += u.getKey();
}
}
public static void main(String[] args) {
Graph graph = new Graph(9);
String[] nodes = new String[9];
nodes[0] = "a";
nodes[1] = "b";
nodes[2] = "c";
nodes[3] = "d";
nodes[4] = "e";
nodes[5] = "f";
nodes[6] = "g";
nodes[7] = "h";
nodes[8] = "i";
int pos = 0;
for (String s: nodes) {
graph.weightedGraph[pos].setLabel(s);
pos += 1;
}
graph.addEdge("a", "b", 4);
graph.addEdge("a", "h", 9);
graph.addEdge("b", "h", 11);
graph.addEdge("b", "c", 8);
graph.addEdge("h", "i", 7);
graph.addEdge("i", "g", 6);
graph.addEdge("c", "f", 4);
graph.addEdge("c", "d", 7);
graph.addEdge("d", "e", 9);
graph.addEdge("d", "f", 14);
graph.addEdge("e", "f", 10);
graph.addEdge("h", "g", 1);
graph.addEdge("c", "i", 2);
graph.addEdge("g", "f", 2);
graph.minimumSpanningTree("a");
}
}
基本上我有三个类Node
,Edge
和Graph
。我Comparator
在 Node 中包含了一个,以允许 PriorityQueue 根据需要重新排序元素。
我构建了图表 call minimumSpanningTree
,它打印了以下 MST:
a
b
c
i
f
d
e
h
g
而不是a-b-c-i-f-g
像我在下面显示的 CLRS 示例中那样做:
我真的不明白为什么它选择节点d
而不是g
当g
明显具有最低键时,通过调试priorityQueue 来证实这一点。
非常感谢您的帮助。
解决方案
我发现了我的问题。事实证明,我构建的邻接表表示中的边是有向的。为了解决这个问题,我只是添加了反向边缘,一切都按预期工作。
并且事实证明,优先级队列在删除其中一个元素时不会更新其元素。根据 SO 关于优先级队列 (PQ) 未更新的其他一些答案,我从 PQ 中删除并添加了其键在 for 循环内更新的节点。有人有更好的建议吗?更新的代码minimumSpanningTree
如下:
public void minimumSpanningTree(String root) {
ArrayList<Node> msTree = new ArrayList<>();
Node rootNode = null;
for (Node vertex: this.weightedGraph) {
vertex.setKey(Integer.MAX_VALUE);
vertex.setDaddy(null);
if (vertex.getLabel().contains(root)) {
rootNode = vertex;
}
}
rootNode.setKey(0);
PriorityQueue<Node> nodePriorityQueue = new PriorityQueue<>();
for (Node vertex: this.weightedGraph) {
nodePriorityQueue.add(vertex);
}
int min = 0;
while (!nodePriorityQueue.isEmpty()) {
Node u = nodePriorityQueue.peek();
LinkedList<Edge> uEdges= u.getEdges();
for (Edge e: uEdges) {
Node v = e.getDestination();
int u_vWeight = e.getWeight();
if (nodePriorityQueue.contains(e.getDestination()) && u_vWeight < v.getKey()) {
nodePriorityQueue.remove(v);
v.setDaddy(u);
v.setKey(u_vWeight);
nodePriorityQueue.add(v);
}
}
msTree.add(u);
System.out.println(u.getLabel());
nodePriorityQueue.remove(u);
}
编辑:但是,我在边缘a-h
和a-b
. b
我认为它仍然是一个 MST ,但是有没有办法可以优先考虑访问节点h
?即,如果优先级队列中存在关系,则优先考虑具有较低字母数字优先级的键?
推荐阅读
- python - Pandas 数据框将整列乘以另一列中的单个单元格,以控制组标识符
- javascript - setState 映射函数中对象的一种状态
- excel - 在工作表中的灵活区域上分隔宏
- url - 如何在控制器 CakePHP 中使用 $this->Html->url?
- css - Angular Material design 复选框黑色前景色(符号颜色)
- java - Couchbase 中不区分大小写的文档 ID 匹配
- sql - 插入查询以同时具有选择和值作为值
- reactjs - 根据所选应用更改外部共享消息
- elasticsearch - Elasticsearch 可以做反向前缀搜索吗?
- excel - VBA 类不支持自动化或不支持预期的接口