一、优先级队列
优先级队列(Priority Queue)是一种出元素具有优先级的队列,例如在出队的时候元素需要出优先级更高的元素。
Java中,优先级队列的底层是通过堆来实现的。
二、堆
堆是一棵特殊的完全二叉树,他的父节点总是大于等于子节点(大根堆) 或者 父节点总是小于等于子节点(小根堆)。
下面我们以大根堆为例来介绍堆的基本操作
三、堆的创建
由于堆是一课完全二叉树,所以适合用顺序存储的方式来存储
public int[] elem;
public int usedSize;
public PriorityQueue(int[] elem) {
this.elem = elem;
usedSize += elem.length;
}
3.1 向下调整
我们使用向下调整的方式使这个树变成大根堆,即从根节点开始,依次将每个节点和它的子树变成大根堆。
public void createBigHeap() {
for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {
siftDown(parent, usedSize);
}
}
//向下调整
private void siftDown(int parent,int end) {
int child = 2*parent+1;
while (child < end) {
if(child + 1 < usedSize && elem[child] < elem[child+1]) {
child++;
}
//child一定是 左右孩子最大值的下标
if(elem[child] > elem[parent]) {
//交换
swap(child,parent);
parent = child;
child = 2*parent+1;
}else {
break;
}
}
}
3.2 建堆的时间复杂度
建堆的时间复杂度为O(N)。
四、堆的插入与删除
4.1 堆的插入
先将待插入元素放入队列末尾元素的后一个位置,如果空间不够需要扩容。然后使用向上调整来保持堆为大根堆。
4.2 向上调整
private void shiftUp(int child) {
int parent = (child-1)/2;
while (child > 0) {
if(elem[child] > elem[parent]) {
swap(child,parent);
child = parent;
parent = (child-1)/2;
}else {
break;
}
}
}
4.3堆的删除
堆的删除一定是只能删除堆顶元素,我们采取将末尾元素与堆顶元素互换的思路,然后useSize--,再进行向下调整。
public int poll() {
int tmp = elem[0];
swap(0,usedSize-1);
usedSize--;
siftDown(0,usedSize);
return tmp;
}