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 中的优先级队列,用于跟踪每个节点的位置的主要内容,如果未能解决你的问题,请参考以下文章

线性表--08---优先队列

笔记2. 堆(优先队列)

数据结构之基于堆的优先队列

Redis之上的分布式Java队列

霍夫曼编码求节省空间

使用最小优先级队列时,如何跟踪 Dijkstra 算法中的最短路径?