堆分最小堆最大堆,优先级队列也分最小优先级队列和最大优先级队列。先来介绍一下优先级队列:
优先级队列是用来维护由一组元素构成的集合S的数据结构,这一组元素中的每一个都有一个关键字key,一个最大优先级队列支持以下操作:
Insert(S, x):把元素x插入S中
Maximun(S):返回S中具有最大关键字的元素
ExtractMax(S):去掉并返回S中的具有最大关键字的元素
IncreaseKey(S, x, k):将元素x的值增加到k,这里的k必须大于等于x
最大优先级队列可以用来对计算机内部作业进行调度。当一个作业做完或者被中断的时候,就由ExtractMax来选出合适的优先级队列。在任何时候,一个新作业都可以有Inset来插入到队列中去。最小优先级其实还有更多的不同的应用,这个我在之后的博客会讲。
接下来是上述四个操作的实现代码:
首先堆的数据结构贴一下:为什么用ArrayList不用数组,这在堆排序的博客讲过,而其实就是在优先级队列的应用中,存在着数组完成不了的事情
public class Heap {
private ArrayList<Integer> A;
private int heapSize;
public ArrayList<Integer> getA() {
return A;
}
public void setA(ArrayList<Integer> a) {
A = a;
}
public int getHeapSize() {
return heapSize;
}
public void setHeapSize(int heapSize) {
this.heapSize = heapSize;
}
}
以下HeapMaximum代码,没什么好解释的,最大堆的根结点就是最大元素,复杂度O(1)
/**
* 返回堆中具有最大关键字的元素
* @param heap 堆
* @return 堆的最大元素
*/
public int HeapMaximum(Heap heap) {
return heap.getA().get(0);
}
以下HeapExtractMax代码,堆大小小于1的时候,队列为空,抛出异常,不然就交换根结点和末尾的一个元素,然后使用MaxHeapify保持最大堆性质,这和HeapSoet的过程很类似。复杂度是O(lgn),这个复杂度来自于MaxHeapify的复杂度。
/**
* 去掉并返回堆中的具有最大关键字的元素
* @param heap 堆
* @return 堆的最大元素
* @throws Exception 堆大小小于1的时候,队列为空,抛出异常
*/
public int HeapExtractMax(Heap heap) throws Exception {
ArrayList<Integer> A = heap.getA();
int heapSize = heap.getHeapSize();
if (heapSize < 1) {
throw new Exception("heap underflow");
}
int max = A.get(0);
A.set(0, A.get(heapSize - 1));
heap.setHeapSize(heapSize - 1);
this.MaxHeapify(heap, 0);
return max;
}
以下HeapIncreaseKey代码,如果key小于下标i的元素值,抛出异常。否则就设置下标i为key,只有这个被改变的值可能会影响堆的性质,所以就与父结点做比较,如果比父结点大,那就和父结点交换。然后再和新的父结点做比较,直到小于父结点。复杂度O(Ign),因为最坏情况是n个元素,从最底下一层交换到根结点。
/**
* 将元素i的值增加到key,这里的key必须大于等于元素i的值
* @param heap 堆
* @param i 目标元素的下标
* @param key 目标值
* @throws Exception 如果key小于下标i的元素值,抛出异常
*/
public void HeapIncreaseKey(Heap heap, int i, int key) throws Exception {
ArrayList<Integer> A = heap.getA();
int heapSize = heap.getHeapSize();
if (key < A.get(i)) {
throw new Exception("new key is smaller than current key!");
}
A.set(i,key);
while (i > 0 && A.get(this.parent(i)) < A.get(i)) { // 跟父节点比较
int temp = A.get(this.parent(i));
A.set(this.parent(i), A.get(i));
A.set(i, temp);
i = this.parent(i);
}
}
把元素i从4增大到15的过程大致如下:
/**
* 把元素key插入堆中
* @param heap 堆
* @param key 目标值
* @throws Exception HeapIncreaseKey所产生的
*/
public void MaxHeapInsert(Heap heap, int key) throws Exception {
ArrayList<Integer> A = heap.getA();
int heapSize = heap.getHeapSize();
heap.setHeapSize(++heapSize);
A.add(Integer.MIN_VALUE);
HeapIncreaseKey(heap, heapSize - 1, key);
}
所以,我们可以在O(lgn)的时间里,完成任意的优先级队列操作,效率还是相当高的。
上面有所有方法的单元测试:https://github.com/qjkobe/IntroductionToAlgorithms
原文:http://blog.csdn.net/qj30212/article/details/52454270
我略微修改了一下代码和描述