优先队列
优先队列要提供并实现两个操作
- 删除并返回最大元素
- 插入元素
优先队列的实现
优先队列的实现采用有序二叉堆形式,采用链表或堆栈也可实现,但时间复杂度较高。这里不再赘述。
有序二叉堆的特点
- 所有根结点必定不小于其两个叶子节点
- 如果某个节点对应的数组下标为k,则其根节点为k/2,其叶子节点为2k和2k+1
- 有序二叉堆通过数组实现,采用数组a[N+1]描述有序二叉堆中第一个到最后一个节点(数组元素a[0]不使用,从a[1]开始)。
- 如图所示:
有序二叉堆的主要操作
- 上浮
当插入新元素到堆中时,先将该元素置于数组末尾,再上浮至正确节点 - 下沉
当移除根节点元素时(假设根节点为最大元素),将数组末尾元素置于根节点,再下沉至正确位置
public class MaxPQ<T extends Comparable<T>>{
private T[] a;
private int index= 0;//指向末尾元素
private int N;//记录数组的大小
public MaxPQ(int size){
N = size;
a = (T[])new Comparable[](size + 1);
}
//插入新元素到堆中
public void insert(T item){
//插入前检测数组是否需要扩容
if (index >= N * 0.75) {
resize(a);
N = a.length;
}
//指针指向新的末尾元素
a[++index] = item;
//上浮至正确节点位置
swim(index);
}
private void swim(int k){
while(k > 1 && a[k] > a[k/2]){
swap(k, k/2);
k = k / 2;//每上浮一次,k要变为其父节点位置
}
}
//从堆中移除最大元素
private T removeMax(){
T max = a[1];
a[1] = a[index];//将末尾元素当作新的根节点
a[index] = null;//防止对象游离
index--;
sink(1);//将根节点元素下沉至正确位置
return max;
}
private T sink(int k){
while(k* 2 < N){
int j = 2 * k;//j记录叶子节点位置
if (a[j] < a[j+1]) {j++};//j移动到两个叶子节点中较大的那个
if (a[k] > a[j]) {break};//如果父节点大于叶子节点,说明位置已找到,跳出循环
swap(k, j);//否则将父节点和叶子节点交换
k = j;
}
}
//数组扩容函数
private void resize(T[] a){
T[] temp = (T[])new Object[N * 2];
for (int i = 0; i < a.length; i++) {
temp[i] = a[i];
}
a = temp;
}
}