优先队列
使用最大堆或者最小堆来实现一个优先队列是非常合适的。这个优先队列需要实现插入新值、改变键值和抽取最大/最小元素的功能。实现如下:
package heap;
public class PriorityQueue <T extends Heap> {
private T heap;
public PriorityQueue(T heap) {
this.heap = heap;
}
public int extract() {
return heap.extract();
}
public void insert(int key) {
heap.insert(key);
}
public void changeKey(int i, int key) {
heap.changeKey(i, key);
}
@Override
public String toString() {
return heap.toLineStr();
}
}
这里我使用了泛型类,虽然不是非常有必要。采用的设计模式是策略模式,策略就是Heap, 可能是最大堆或者最小堆。它主要有下面三个接口:
1. extract: 抽取最大或者最小元素,并保证原有堆的性质不变。
2. insert: 插入新的键值。
3. changeKey: 对于最大堆来说,是增大键值。对于最小堆来说,是减小键值。
extract
它定义在Heap里面,实现起来很简单,就是抽取items[0],并将items的最后一个元素换到前面来,并调用heapify方法,保持堆的性质不变。代码如下:
public abstract class Heap {
// 取出最大元素,并恢复其为最大堆或者最小堆
public int extract() {
if (heapSize < 1) throw new RuntimeException("heap underflow.");
int tmp = items[0];
items[0] = items[--heapSize];
heapify(0);
return tmp;
}
}
changeKey
public abstract class Heap {
// 将第i个元素的值改掉
public abstract void changeKey(int i, int key);
}
这个方法,对于最大堆来说,是increaseKey;对于最小堆来说,是decreaseKey方法。实现思路是,将当前元素与父元素比较,如果不满足堆性质,将它们进行调换,并继续往父元素比较。这个方法其实是有问题的,因为无法获得i的值,所以具体使用的时候,要么对items进行改进,增加对应下标i的属性;要么在堆中建立从key到i的映射关系。
public class MaxHeap extends Heap {
// increase key
@Override
public void changeKey(int i , int key) {
if (i >= heapSize) throw new RuntimeException("invalid index");
// 必须是增大的
if (key < items[i]) throw new RuntimeException("key smaller than original value");
items[i] = key;
// i > 0 等价于 parent(i)>-1;
while(i > 0 && items[parent(i)] < items[i]) {
int tmp = items[parent(i)];
items[parent(i)] = items[i];
items[i] = tmp;
i = parent(i);
}
}
}
public class MinHeap extends Heap{
@Override
public void changeKey(int i, int key) {
if (i >= heapSize) throw new RuntimeException("invalid index");
if (key > items[i]) throw new RuntimeException("key larger than original value");
items[i] = key;
while(i > 0 && items[parent(i)] > items[i]) {
int tmp = items[parent(i)];
items[parent(i)] = items[i];
items[i] = tmp;
i = parent(i);
}
}
}
insert
public abstract class Heap {
// 插入元素
public abstract void insert(int key);
}
插入新值。实现思路:在heap末尾新增一个值,再调用changeKey方法。
public class MaxHeap extends Heap {
// negative infinity;
@Override
public void insert(int key) {
if (heapSize+1 > items.length) throw new RuntimeException("overflow");
heapSize++;
items[heapSize-1] = Integer.MIN_VALUE;
changeKey(heapSize-1, key);
}
}
public class MinHeap extends Heap{
// positive infinity
@Override
public void insert(int key) {
if (heapSize+1 > items.length) throw new RuntimeException("heap overflow");
heapSize++;
items[heapSize-1] = Integer.MAX_VALUE;
changeKey(heapSize-1, key);
}
}