1.堆的实质
堆其实是二叉树按照层序遍历放在一个数组中的表示方法。
如果是堆,它有以下性质:
- 堆逻辑上是一棵完全二叉树
- 堆物理上是保存在数组中
- 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
- 反之,则是小堆,或者小根堆,或者最小堆。
2.求双亲结点和左右子树的公式
已知双亲(parent)的下标,则:
左孩子(left)下标 = 2 * parent + 1;
右孩子(right)下标 = 2 * parent + 2;
已知孩子(不区分左右)(child)下标,则:
双亲(parent)下标 = (child - 1) / 2;
3.堆的向下调整
首先是小堆的调整,拿到一个结点去求它的左右子树,如果左右子树在这个数组中,就说明要进行一次调整,并且比较左右子树的值,找出左右子树中最小的,并把下标记下来。之后再把index所在处的值和最小的值进行比较,如果index所在处的值大于最小的值,把这两个数进行交换,之后index更新为之前最小值的下标,left进行一次更新;如果小于,跳出循环,调整结束。
代码如下:
public static void shiftDownSmall(int[] array,int size,int index) {
int left = 2 * index + 1;
while (left < size) {
int right = left + 1;
int min = left;
if (right < size) {
if (array[right] < array[left]) {
min = right;
}
}
if (array[index] > array[min]) {
swap(array, index, min);
index = min;
left = 2 * index + 1;
} else {
break;
}
}
}
其次是大堆的调整,和小堆的调整差不多,拿到一个结点去求它的左右子树,如果左右子树在这个数组中,就说明要进行一次调整,并且比较左右子树的值,找出左右子树中最大的,并把下标记下来。之后再把index所在处的值和最大的值进行比较,如果index所在处的值小于最大的值,把这两个数进行交换,之后index更新为之前最大值的下标,left进行一次更新;如果大于,跳出循环,调整结束。
代码如下:
public static void shiftDownBig(int[] array, int index, int size){
int left=2*index+1;
while(left<size){
int right=left+1;
int max=left;
if(array[right]<size&&array[right]>array[left]){
max=right;
}
if(array[index]<array[max]){
swap(array,index,max);
index=max;
left=2*index+1;
}else{
break;
}
}
}
4.堆的向上调整
拿调整小堆的例子来说,只要它的双亲结点还大于子树的结点,就交换位置并且更新下标。
调整大堆是一样的道理,这里就不再举例。
public static void shiftUpSmall(int[] arr,int i){
while(i!=0){
int parent=(i-1)/2;
if(arr[parent]>arr[i]){
swap(arr,parent,i);
i=parent;
}else{
break;
}
}
}
5.优先队列
(1)概念
在很多应用中,我们通常需要按照优先级情况对待处理对象进行处理,比如首先处理优先级最高的对象,然后处理次高的对象。最简单的一个例子就是,在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话。
在这种情况下,我们的数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象。
这种数据结构就是优先级队列。
(2)内部操作(用堆来实现)
- 操作-入队列
过程(以大堆为例):
- 首先按尾插方式放入数组
- 比较其和其双亲的值的大小,如果双亲的值大,则满足堆的性质,插入结束
- 否则,交换其和双亲位置的值,重新进行 2、3 步骤
- 直到根结点
public void offer(int element){
array[size]=element;
size++;
Heap.shiftUpSmall(array, size - 1);
}
- 操作-出队列
为了防止破坏堆的结构,删除时并不是直接将堆顶元素删除,而是用数组的最后一个元素替换堆顶元素,然后通过
向下调整方式重新调整成堆
public int poll(){
int element=array[0];
size--;
array[0]=array[size];
Heap.shiftDownSmall(array,size,0);
return element;
}
整体代码:
import java.util.PriorityQueue;
public class MyPriorQueue {
// 不做扩容考虑
private int[] array;
private int size;
MyPriorQueue() {
array = new int[16];
size = 0;
}
public void offer(int element){
array[size]=element;
size++;
Heap.shiftUpSmall(array, size - 1);
}
public int poll(){
int element=array[0];
size--;
array[0]=array[size];
Heap.shiftDownSmall(array,size,0);
return element;
}