Java:我的 Prim 看起来怎么样?
我正在尝试用 JGraphT 实现 Prim 的最小生成树算法。看起来怎么样?
我遇到的一个问题是 JGraphT 按其指示处理所有事情。因此,有时有必要进行一些尴尬的调用来反转 g.getEdgeSource(e)
和 g.getEdgeTarget(e)
(如果它们碰巧不正确)。
我最初尝试使用 JGraphT 的斐波那契堆来实现这一点,但它太难了,所以我只做了一个常规的 PQ。
我没有将不存在的边的权重设置为无穷大,而是没有将其添加到队列中。
建议?风格问题?明显的低效率?我应该使用代码而不是自己编写代码?
public static Graph<String, DefaultWeightedEdge> primPQ(final WeightedGraph<String, DefaultWeightedEdge> g, String root) {
Graph<String, DefaultWeightedEdge> mst = new SimpleWeightedGraph<String, DefaultWeightedEdge>(DefaultWeightedEdge.class);
Queue<DefaultWeightedEdge> pq = new PriorityQueue<DefaultWeightedEdge>(g.vertexSet().size(), new Comparator<DefaultWeightedEdge>() {
@Override
public int compare(DefaultWeightedEdge o1, DefaultWeightedEdge o2) {
if (g.getEdgeWeight(o1) < g.getEdgeWeight(o2)) {
return -1;
}
if (g.getEdgeWeight(o1) > g.getEdgeWeight(o2)) {
return 1;
}
return 0;
}
});
mst.addVertex(root);
DefaultWeightedEdge link;
for (String v : g.vertexSet()) {
link = g.getEdge(root, v);
if (link != null) {
pq.add(link);
}
}
//this is made difficult by JGraphT's assumption that everything is directed
DefaultWeightedEdge minEdge = pq.poll();
String toAdd;
String alreadyFound;
String tmp;
while (minEdge != null) {
// guessing at which is in the MST
toAdd = g.getEdgeTarget(minEdge);
alreadyFound = g.getEdgeSource(minEdge);
if (!(mst.containsVertex(toAdd) && mst.containsVertex(alreadyFound))) {
// swap if backwards
if (mst.containsVertex(toAdd)) {
tmp = toAdd;
toAdd = alreadyFound;
alreadyFound = tmp;
}
mst.addVertex(toAdd);
mst.addEdge(alreadyFound, toAdd, minEdge);
System.out.format("%s --> %s\n", g.getEdgeSource(minEdge), toAdd);
for (String v : g.vertexSet()) {
if (! mst.containsVertex(v)) {
link = g.getEdge(toAdd, v);
if (pq.contains(link)) {
g.setEdgeWeight(minEdge, Math.min(g.getEdgeWeight(minEdge), g.getEdgeWeight(link)));
}
if (link != null && ! pq.contains(link)) {
pq.add(link);
}
}
}
}
minEdge = pq.poll();
}
return mst;
}
I am trying to implement Prim's minimum spanning tree algorithm with JGraphT. How does it look?
One issue I ran into was JGraphT's handling of everything like it's directed. So sometimes it is necessary to make some awkward calls to reverse g.getEdgeSource(e)
and g.getEdgeTarget(e)
if they didn't happen to be right.
I tried to implement this originally with JGraphT's Fibonacci Heap but it was too hard so I just did a regular PQ.
Instead of setting weights of non-existent edges to infinity, I just didn't add it to the queue.
Advice? Stylistic issues? Glaring inefficiencies? Code I should be using instead of rolling my own?
public static Graph<String, DefaultWeightedEdge> primPQ(final WeightedGraph<String, DefaultWeightedEdge> g, String root) {
Graph<String, DefaultWeightedEdge> mst = new SimpleWeightedGraph<String, DefaultWeightedEdge>(DefaultWeightedEdge.class);
Queue<DefaultWeightedEdge> pq = new PriorityQueue<DefaultWeightedEdge>(g.vertexSet().size(), new Comparator<DefaultWeightedEdge>() {
@Override
public int compare(DefaultWeightedEdge o1, DefaultWeightedEdge o2) {
if (g.getEdgeWeight(o1) < g.getEdgeWeight(o2)) {
return -1;
}
if (g.getEdgeWeight(o1) > g.getEdgeWeight(o2)) {
return 1;
}
return 0;
}
});
mst.addVertex(root);
DefaultWeightedEdge link;
for (String v : g.vertexSet()) {
link = g.getEdge(root, v);
if (link != null) {
pq.add(link);
}
}
//this is made difficult by JGraphT's assumption that everything is directed
DefaultWeightedEdge minEdge = pq.poll();
String toAdd;
String alreadyFound;
String tmp;
while (minEdge != null) {
// guessing at which is in the MST
toAdd = g.getEdgeTarget(minEdge);
alreadyFound = g.getEdgeSource(minEdge);
if (!(mst.containsVertex(toAdd) && mst.containsVertex(alreadyFound))) {
// swap if backwards
if (mst.containsVertex(toAdd)) {
tmp = toAdd;
toAdd = alreadyFound;
alreadyFound = tmp;
}
mst.addVertex(toAdd);
mst.addEdge(alreadyFound, toAdd, minEdge);
System.out.format("%s --> %s\n", g.getEdgeSource(minEdge), toAdd);
for (String v : g.vertexSet()) {
if (! mst.containsVertex(v)) {
link = g.getEdge(toAdd, v);
if (pq.contains(link)) {
g.setEdgeWeight(minEdge, Math.min(g.getEdgeWeight(minEdge), g.getEdgeWeight(link)));
}
if (link != null && ! pq.contains(link)) {
pq.add(link);
}
}
}
}
minEdge = pq.poll();
}
return mst;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我将你的算法的结果与我的一个作业进行了比较,它们都给出了相同的最小总权重,坚持下去!
I have compared the result of your algorithm to one mine which was a homework and it both gives the same minimum total weight, keep it up !
在增加边数和顶点数的同时,我得到了不同的结果,我会回来的......
Both while increasing the number of edges and the number of vertices, I get different results, I'll be back...
好吧,现在看起来不错。
顺便说一句,我的练习有点棘手:我必须编写一个算法来确实找到最小生成树,但我的算法必须通过边缘消除来进行。我写了一些东西,使用一些深度优先遍历来查找循环,然后通过将其“着色”为红色来消除权重最大的边。
对于 4 个随机生成的 N=200 个节点的图,您的 PRIM 算法产生的最小总权重与我的算法相同以及边数的各种值M=(N*(N-1)/k),其中k=2,3,4,8。
乍一看,我认为这种测试太过分了,但事实并非如此!
OKAY, it looks fine now.
By the way, my exercise was a bit tricky: I had to write an algorithm that finds the minimum spanning tree indeed, but my algorithm had to proceed by elimination on the edges. I wrote something that uses some depth first traversal to find cycles and then eliminates the most weighted edge by "coloring" it red..
Your PRIM alg yields the same minimum total weight than my alg for 4 randomly generated graphs that have N=200 nodes and various values M=(N*(N-1)/k) for k=2,3,4,8 for the number of edges.
At first sight, I would have tought this kind of testing was overkill, but it was not !