为了达到O(ElogV)的效率,需要对普利姆算法进行eager实现。
如果我们用java来做,jdk当中的priorityQueue并不能满足我们的要求。
因为我们需要进行一个对索引元素降key的操作(decrease-key).
/**
* 将索引所关联的key降到newKey
*
* @param index 索引
* @param newKey 新的key
*/
public void decreaseKey(int index, E newKey) {
if (index < 0 || index >= queue.length)
throw new IndexOutOfBoundsException();
if (newKey == null)
throw new NullPointerException();
if (!contains(index))
throw new NoSuchElementException("指定的索引不存在!");
if (this.comparator() != null) {
if (this.comparator().compare((E) keys[index], newKey) <= 0)
throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
} else {
Comparable<? super E> key = (Comparable<? super E>) getKeyOf(index);
if (key.compareTo(newKey) <= 0)
throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
}
keys[index] = newKey;
siftUp(getPositionOf(index));
}
由于需要知道优先队列中元素的索引以支持外部索引式访问,我们将对priorityQueue中的Queue建立索引,并将此索引当作二叉堆实际存储的元素,而原来的元素我们通过索引表来访问它.
/**
* 索引所关联的keys
*/
private transient Object[] keys;
/**
* 建立一个从索引在二叉堆中的位置到索引的映射
*/
private transient int[] queue;
这里还有一个问题,就是在进行上浮(siftUp)和下沉(siftDown)时,需要对堆中的元素和位置进行互访,我们还需要建立一个索引到位置的倒排,或者索引到位置的一个映射.
/**
* 建立一个从索引到其在二叉堆中位置的映射,下标是关联key的索引
*/
private transient int[] postQueue;
这样我们就可以按下面的方式访问二叉堆中的索引、它在二叉堆中的位置、以及它关联的key:
/**
* 返回给定位置的索引
*
* @param position 二叉堆中的位置
* @return 给定位置的索引
*/
private int getIndexOf(int position) {
return queue[position];
}
/**
* 返回给定索引在二叉堆中的位置
*
* @param index 索引
* @return 给定索引在二叉堆中的位置
*/
private int getPositionOf(int index) {
return postQueue[index];
}
/**
* 返回给定索引所关联的key
*
* @param index 索引
* @return 索引所关联的key
*/
private E getKeyOf(int index) {
return (E) keys[index];
}
而交换元素时,需要先交换索引,然后再维护索引到位置的映射:
/**
* 按位置交换
*
* @param x 一个元素的位置
* @param y 另一个元素的位置
*/
private void exchangeByPosition(int x, int y) {
int tmp = queue[x];
queue[x] = queue[y];
queue[y] = tmp;
postQueue[queue[x]] = x;
postQueue[queue[y]] = y;
}
这样,我们的上浮操作看起来是这样的:
private void siftUpComparable(int k) {
Comparable<? super E> key = (Comparable<? super E>) keys[getIndexOf(k)];
while (k > 0) {
int parent = (k - 1) >>> 1;
Object e = keys[queue[parent]];
if (key.compareTo((E) e) >= 0)
break;
exchangeByPosition(k, parent);
k = parent;
}
}
而下沉操作就变成了这样:
<