一 二叉堆概述
二叉堆实际上就是一种特殊的二叉树,完全二叉树,储存为数组结构,如图所示
数组下标由1开始,是为了更好地描述子父关系
/**
* 获取父节点索引
* @param index
* @return
*/
private int parent(int index){
//等价于 index/2
return index>>1;
}
/**
* 获取下级左节点索引
* @param index
* @return
*/
private int left(int index){
//等价于index*2
return index << 1;
}
/**
* 获取下级右节点索引
* @param index
* @return
*/
private int right(int index){
//等价于index*2 + 1
return (index << 1)+1;
}
二叉堆区分为最大堆、最小堆,最大堆特性每个节点都大于它的两个子节点,最小堆特性每个节点都小于它的两个子节点。根据其特性,最大堆堆顶一定是堆中最大值,最小堆堆顶一定是堆中最小值,本文主要讲解最大堆的实现。
二 优先队列
1.概述
优先队列是一种数据结构,插入与删除元素根据优先级自动排序,下面我们根据最大堆原理实现优先队列,为保证最大堆特性,主要通过上浮与下沉实现
public class MaxPQ<key extends Comparable<key>> {
/**
* 存储元素数组
*/
private key[] pq;
/**
* 当前【优先队列】元素个数
*/
private int n;
/**
* 初始化示例
* @param capacity 数组容量 去除下标为0 实际 capacity+1
*/
private MaxPQ(int capacity){
pq = (key[]) new Comparable[capacity+1];
}
/**
* 返回当前队列中最大的元素
* @return
*/
private key max(){
return pq[1];
}
/**
* 队列插入元素
* 添加元素到最后 调用方法上浮
* @param e
*/
private void insert(key e){
n++;
pq[n] = e;
swim(n);
}
/**
* 删除并返回当前队列中最大元素
*/
private key delMax(){
// 1.交换堆顶与堆底值
key max = max();
swap(1,n);
// 2.删除堆底值
pq[n] = null;
n--;
// 3.下沉调换后堆顶值
sink(1);
return max;
}
/**
* 上浮第k个元素,以维护最大堆性质
* @param k
*/
private void swim(int k){
// k不等于堆最大值且队列第k个元素值大于父节点
while (k>1 && less(parent(k),k)){
//交换父节点值 循环上浮
swap(parent(k),k);
k = parent(k);
}
}
/**
* 下沉第k个元素,以维护最大堆性质
* @param k
*/
private void sink(int k){
//如果沉到堆底不再下沉
while (left(k)<=n){
// 1.比较子节点左右值大小,获取较大子节点
int order = left(k);
if (right(k)<=n && less(order,right(k))){
order = right(k);
}
// 2.当前值比两个子节点都大,无需交换
if (less(order,k)){
break;
}
// 3.与子节点交换值 循环处理
swap(order,k);
k = order;
}
}
/**
* 交换两个数组
* @param i
* @param j
*/
private void swap(int i, int j){
key temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
}
/**
* 比较两个队列元素大小 pq[i] 是否小于 pq[j]
* @param i
* @param j
* @return
*/
private Boolean less(int i,int j){
return pq[i].compareTo(pq[j])<0;
}
/**
* 获取父节点索引
* @param index
* @return
*/
private int parent(int index){
//等价于 index/2
return index>>2;
}
/**
* 获取下级左节点索引
* @param index
* @return
*/
private int left(int index){
//等价于index*2
return index << 1;
}
/**
* 获取下级右节点索引
* @param index
* @return
*/
private int right(int index){
//等价于index*2 + 1
return (index << 1)+1;
}
}
2.插入元素(insert)上浮(swim)调整
在堆底插入元素,假设元素值大于父节点的值,此时最大堆特性被破坏,则需上浮与父节点调换位置,调整后仍然不满足则需循环上浮,如图所示
3.删除堆顶元素(delMax)下沉(sink)调整
删除堆顶元素,把堆顶元素与堆中最后元素对调,删除,然后通过下沉堆顶元素达到堆平衡,如图所示