Java 中的优先级队列,用于跟踪每个节点的位置
Posted
技术标签:
【中文标题】Java 中的优先级队列,用于跟踪每个节点的位置【英文标题】:Priority Queue in Java which keeps track of each node's position 【发布时间】:2014-05-05 01:01:25 【问题描述】:所以我正在为我的班级建立一个实验室。我需要使用数组列表在 Java 中创建优先级队列。关键是每个节点都必须有一个“句柄”,它只是一个对象,其中包含与其关联的节点的索引。我知道这听起来很奇怪,但我们必须以这种方式实现它。无论如何,我的优先级队列似乎工作正常,除了句柄值显然没有正确更新,因为我没有通过句柄测试。另外,我只在调用 extractMin() 后遇到了句柄问题,所以我认为问题可能出在该方法的某个地方,但我已经经历过一百万次了,对我来说看起来不错。
这是我的优先队列类:
package lab3;
import java.util.ArrayList;
/**
* A priority queue class supporting operations needed for
* Dijkstra's algorithm.
*/
class PriorityQueue<T>
final static int INF = 1000000000;
ArrayList<PQNode<T>> queue;
/**
* Constructor
*/
public PriorityQueue()
queue = new ArrayList<PQNode<T>>();
/**
* @return true iff the queue is empty.
*/
public boolean isEmpty()
if (queue.size() <= 0)
return true;
return false;
/**
* Insert a (key, value) pair into the queue.
*
* @return a Handle to this pair so that we can find it later.
*/
Handle insert(int key, T value)
queue.add(new PQNode(key, value));
int loc = queue.size()-1;
// Swap with parent until parent not larger
while (loc > 0 && queue.get(loc).getKey() < queue.get(parent(loc)).getKey())
swap(loc, parent(loc));
loc = parent(loc);
return new Handle(loc);
private void swap(int i, int j)
PQNode t = queue.get(i);
queue.set(i, queue.get(j));
queue.get(i).setHandleIndex(i);
queue.set(j, t);
queue.get(j).setHandleIndex(j);
/**
* @return the min key in the queue.
*/
public int min()
if (isEmpty())
return -INF;
return queue.get(0).getKey();
/**
* Find and remove the smallest (key, value) pair in the queue.
*
* @return the value associated with the smallest key
*/
public T extractMin()
if (isEmpty())
return null;
else
T value = queue.get(0).getValue();
swap(0, queue.size()-1);
queue.get(queue.size()-1).setHandleIndex(-1);
queue.remove(queue.size()-1);
heapify(0);
return value;
private void heapify(int i)
int left = leftChild(i);
int right = rightChild(i);
int min;
if (left <= queue.size()-1 && queue.get(left).getKey() < queue.get(i).getKey())
min = left;
else
min = i;
if (right <= queue.size()-1 && queue.get(right).getKey() < queue.get(min).getKey())
min = right;
if (min != i)
swap(i, min);
heapify(min);
private static int parent(int i)
return (i-1)/2;
private static int leftChild(int i)
return 2*i + 1;
/**
* @return the key associated with the Handle.
*/
public int handleGetKey(Handle h)
if (h.getIndex() >= 0)
return queue.get(h.getIndex()).getKey();
return -INF;
/**
* Decrease the key to the newKey associated with the Handle.
*
* If the pair is no longer in the queue, or if its key <= newKey,
* do nothing and return false. Otherwise, replace the key with newkey,
* fix the ordering of the queue, and return true.
*
* @return true if we decreased the key, false otherwise.
*/
public boolean decreaseKey(Handle h, int newKey)
if (h.getIndex() == -1) //negative 1 is my signal for an invalidated handle
return false;
if (newKey > queue.get(h.getIndex()).getKey())
return false;
queue.get(h.getIndex()).setKey(newKey); // set the new key
correct(h.index); // restore the min-heap property
return true;
public void correct(int i)
while (i > 0 && queue.get(parent(i)).getKey() > queue.get(i).getKey())
swap(i, parent(i));
i = parent(i);
/**
* Return the index of the right child of node i.
* @param i index of parent
* @return the index of the right child of node i
*/
private static int rightChild(int i)
return 2*i + 2;
/**
* @return the value associated with the Handle.
*/
public T handleGetValue(Handle h)
if (h.getIndex() >= 0)
return queue.get(h.getIndex()).getValue();
else
return null;
还有我的 PQNode 类:
package lab3;
public class PQNode<T>
int key;
T value;
Handle handle;
PQNode(int k, T v)
key = k;
value = v;
handle = new Handle(0);
/**
* @return the key
*/
public int getKey()
return key;
/**
* @param key the key to set
*/
public void setKey(int key)
this.key = key;
/**
* @return the value
*/
public T getValue()
return value;
/**
* @param value the value to set
*/
public void setValue(T value)
this.value = value;
public void setHandle(Handle handle)
this.handle = handle;
/**
* @return the handle
*/
public Handle getHandle()
return handle;
public int getHandleIndex()
return handle.getIndex();
public void setHandleIndex(int i)
handle.setIndex(i);
还有我的句柄类:
package lab3;
public class Handle
int index;
public Handle(int i)
index = i;
/**
* @return the index
*/
public int getIndex()
return index;
/**
* @param index the index to set
*/
public void setIndex(int index)
this.index = index;
因为这是学校作业,我希望能得到提示,但尽量不要只是脱口而出答案。另外,如果您愿意,我也可以发布测试,但我不知道这是否会有很大帮助。只要知道队列按原样工作,但在使用 extractMin() 后句柄不会指向正确的节点。如果您想了解更多信息,请告诉我,我知道这有点模糊。非常感谢各位。
编辑:这是我失败的特定测试
public void basicHandleAndDecreaseTest()
PriorityQueue<String> q = new PriorityQueue<String>();
Map<String, Handle> valuesToHandles = new HashMap<String, Handle>();
Map<String, Integer> valuesToKeys = new HashMap<String, Integer>();
int seq = 0;
// Generate random numbres for the keys.
for (int i = 0; i < 20; i++)
int key = RAND.nextInt(100);
String val = Integer.toString(seq); // guarantees that the value is unique.
seq++;
valuesToKeys.put(val, key);
// Keep track of handles and build queue.
for (Map.Entry<String, Integer> entry : valuesToKeys.entrySet())
int key = entry.getValue();
String value = entry.getKey();
Handle h = q.insert(key, value);
assertEquals(key, q.handleGetKey(h));
assertEquals(value, q.handleGetValue(h));
valuesToHandles.put(value, h);
// Remove some of the elements.
for (int i = 0; i < 5; i++)
String val = q.extractMin();
valuesToKeys.remove(val);
valuesToHandles.remove(val);
// Make sure the handles are still valid.
for (Map.Entry<String, Integer> entry : valuesToKeys.entrySet())
int key = entry.getValue();
String value = entry.getKey();
Handle h = valuesToHandles.get(value);
assertEquals(value, q.handleGetValue(h)); <--Here is when I fail
// Add some more elements.
int limit = seq + 5;
for (int i = seq; i < limit; i++)
int key = RAND.nextInt(100);
String val = Integer.toString(seq);
seq++;
valuesToKeys.put(val, key);
Handle h = q.insert(key, val);
valuesToHandles.put(val, h);
// Decrease keys unnecessarily and verify that they still work.
for (Map.Entry<String, Integer> entry : valuesToKeys.entrySet())
int key = entry.getValue();
String value = entry.getKey();
Handle h = valuesToHandles.get(value);
q.decreaseKey(h, key + 1);
assertEquals(key, q.handleGetKey(h));
assertEquals(value, q.handleGetValue(h));
// Change keys randomly, and check that the values remain attached to handles.
for (Map.Entry<String, Integer> entry : valuesToKeys.entrySet())
int key = entry.getValue();
String value = entry.getKey();
Handle h = valuesToHandles.get(value);
int newKey = RAND.nextInt(100);
q.decreaseKey(h, newKey);
if (newKey < key)
assertEquals(newKey, q.handleGetKey(h));
else
assertEquals(key, q.handleGetKey(h));
assertEquals(value, q.handleGetValue(h));
【问题讨论】:
句柄值怎么没有正确更新?它们是否具有荒谬的值、相差一倍的值、根本不改变或其他什么? +1 提出了一个很好的家庭作业问题,遗憾的是这里太少见了。 句柄似乎总是比它们应该的要少,但是测试会生成一堆随机节点,插入它们,然后提取一些,而不是重新检查句柄以查看是否它们仍然有效,这就是我失败的地方。它们的差异量似乎是随机的 为什么手柄会一样?你已经在堆中移动了东西。也许您不应该在这样做时更新句柄?测试如何“重新检查句柄”? 不,我必须更新句柄。手柄不应该保持不变。它们应该改变,测试知道它们应该如何改变。 【参考方案1】:不用脱口而出,插入时返回的句柄应该是刚刚插入的节点的句柄,而不是新的。这样,当您进行交换时,它将得到更新。节点的句柄也需要初始化为比零更相关的值。
这将使您进入下一个错误... 或者:
测试程序中的一个错误,在标记为“不必要地减少密钥并验证它们是否仍然有效”的块中。正在测试错误的“键”值。节点中的键已更改为key+1,
,因此这是应该测试的内容,而不是key.
这同样适用于标记为“随机更改键,并检查值是否仍然附加到句柄”的块,只是这次新密钥是随机的,不是key+1.
或
decreaseKey()
的含义让我无法理解。
【讨论】:
好的,所以我用这个替换了我的返回语句:return queue.get(loc).getHandle();对于句柄初始化,我可能会将它设置为队列的最后一个索引吗?也非常感谢你 哦,实际上我认为确实如此,现在我只需要正确实现 reductionKey,但您的解决方案解决了这个问题。非常感谢。我真的很感激。decreaseKey()
应该做什么?
它应该只是将任何给定节点的键更新为指定的键,我假设它应该低于当前键。让我将方法与包含详细信息的方法标头一起添加到代码中。我发布问题时忘记包含它。
确定您的decreaseKey()
方法需要工作。测试程序没问题。您没有发布 contains()
或 correct()
方法,但包含 contains()
调用的行没有正确实现上面评论中表达的约束。 correct()
方法似乎只是 heapify().
的另一个名称以上是关于Java 中的优先级队列,用于跟踪每个节点的位置的主要内容,如果未能解决你的问题,请参考以下文章