Prim算法优先队列
Posted
技术标签:
【中文标题】Prim算法优先队列【英文标题】:Prim Algorithm Priority Queue 【发布时间】:2015-04-28 16:02:53 【问题描述】:我正在尝试使用优先级队列在 Java 中实现 Prim 算法。
我找不到我的错误。 :/我只是认识到队列没有正确排序节点。
图表示例:
0 4 7 5
4 0 2 3
7 2 0 1
5 3 1 0
它总是将节点 4 作为第二个节点。所以它像[node1,node4,node2,node3]而不是[node1,node2,node3,node4]对队列进行排序。 我对优先级队列做错了什么?
问候
public class PrimAlgorithm
private static int[] par; // parent
private static int[] key; // value
private static int sum;
public static void prim(Graph g)
Node[] nodes = g.getNodes();
key = new int[g.getMatrix().length];
par = new int[g.getMatrix().length];
PriorityQueue<Node> queue = new PriorityQueue<Node>(42, new Comparator<Node>()
public int compare(Node v1, Node v2)
return Integer.valueOf(key[v1.getId()-1]).compareTo(Integer.valueOf(key[v2.getId()-1]));
for (Node n : nodes)
int x = n.getId()-1;
key[x] = 1000;
par[x] = 0;
queue.add(n);
key[0] = 0;
while(!queue.isEmpty())
Node n = queue.poll();
List<Node> neighbours = n.getNeighbors();
for (Node m : neighbours)
if ( queue.contains(m) && g.getEdge(n, m).getWeight() !=0 && g.getEdge(n, m).getWeight() < key[m.getId()-1])
par[m.getId()-1] = n.getId();
key[m.getId()-1] = g.getEdge(n, m).getWeight();
for (int i=0; i < key.length; i++)
sum += key[i];
System.out.println("Das Gewicht des minimalen Spannbaumes lautet: " + sum);
System.out.println("Der Spannbaum ergibt sich wie folgt: " );
//fängt ab 1 an sonst, hätten wir immer noch einen Nullknoten
for(int i=0; i <par.length; i++)
System.out.println("Der Vorgänger von Knoten: " + " "+ (i+1) + "-> " + par[i] + " Gewicht "
+ key[i]);
public static void main(String[] args)
System.out.println("Prim Algorithmus zu Berechnung des minimalen Spannbaums.");
Graph g = new Graph();
prim(g);
【问题讨论】:
【参考方案1】:一些事情:
-
PriorityQueue 的默认实现不能动态地重新排序队列中的项目。换句话说,当你在添加项目后更改键时,它不会导致队列中的项目改变它们的顺序。
当您第一次将节点添加到 PriorityQueue 时,它们都具有相同的优先级。因此,根据 PriorityQueue 的 API,
如果多个元素以最小值绑定,则头部是这些元素之一——绑定被任意打破。
所以不能保证节点的初始顺序。
-
如果您想要一个高效的 Prim 实现,您不应该使用 PriorityQueue 的 contains() 方法来检查队列内部,因为这是一个 O(N) 操作。相反,使用布尔数组来跟踪队列中的哪些项目是 O(1) 查找。
对于重新排序队列的有效方法,请注意添加操作是 O(log(n)),这是有效的,而从队列前面以外的任何地方删除操作是 O(n),这应该是避免。因此,一个好的技巧是保留一个布尔值visited[] 数组,如果节点i 已经被处理,那么visited[i] 为真。然后,您可以多次添加同一个节点,知道将首先检索具有最低键的节点。如果当你在队列中轮询一个节点时,visited[node.id] 已经为真,那么直接跳过它。
当然,为了让它工作,节点必须基于它包含的一些值而不是外部数组进行比较,这样你就可以在队列中拥有两个具有相同 id 但具有不同键的节点。
【讨论】:
感谢您的回答。 3 号是一个很好的提示。但是我怎么知道队列的顺序?我读到可以删除并再次添加节点,但这不是很有效 @Loretta 斐波那契堆怎么样?你可以用更少的计算成本来改变它的值 @rpax 斐波那契堆是来自 org.jgrapht.util 的一个类,我不想使用图形类并自己实现它 @Loretta 斐波那契堆它是斐波那契堆。一个链表它是一个链表。一个哈希图它是一个哈希图。您可以自己实现所有这些。 @rpax 对不起,我以为你的意思是使用已经定义的堆。是否可以为此使用数组并为此定义排序,我的意思是这很聪明吗? (这样算法是有效的)以上是关于Prim算法优先队列的主要内容,如果未能解决你的问题,请参考以下文章