基本概念
二叉堆又名堆,或者优先队列。一般实现在堆顶的元素总是最小的。
二叉堆是一颗用数组实现的完全二叉树。
要实现二叉堆必须满足以下条件:
1堆有序,二叉树中每一个子树的父节点不大于(大根堆)两个子节点。
2树的父节点的两个子节点位置为 2k和2k+1(不使用数组的0号索引),如果使用0号索引,两个子节点的位置为2*k+1和2*k=2。
要实现堆有序,主要依靠上浮(swim)和下沉(snik)两个操作。
在入队时,将数据插入到元素序列尾部,然后将元素上浮到应有的位置。
在出队时,将堆顶最小的元素删除,用元素序列尾部的元素交换到堆顶,然后进行下沉操作,恢复堆有序。
实现
/**
* 二叉堆,小堆,降序
* @author yuli
*
*/
public class BinaryHeap<T extends Comparable<T>>{
private Comparable<T>[] data;
private int size;
private static final int DEFAULT_CAPACITY = 10;
public BinaryHeap() {
clean();
}
public void clean(){
size = 0;
swap(DEFAULT_CAPACITY);
}
/**
* 添加元素,并将元素上浮
* @param t
*/
public void insert(T t){
if(size+1 >= data.length){
swap(2*size +1);
}
//0号位置要空着,所以+了再放元素
data[++size] = t;
swim(size);
}
/**
* 删除元素,并恢复堆有序
* @return
*/
public Comparable<T> delete(){
if(size < 0){
System.out.println("没元素啦");
return null;
}
//获得堆顶元素
Comparable<T> t = data[1];
//将堆顶元素替换成堆底元素
data[1] = data[size];
//移除堆底元素,顺便把容器的大小-1
data[size--] = null;
//将交换来的元素下沉到合适的位置,恢复堆的有序性
sink(1);
return t;
}
/**
* 上浮元素,如果父节点大于子节点,就上浮元素
* @param k 元素的索引
*/
public void swim(int k){
//如果子元素不是头结点,并且父节点大于子节点就交换
while(k > 1 && less(data[k],data[k/2])){
exchange(data, k, k/2);
k = k/2;
}
}
private void exchange(Comparable<T>[] t,int v,int w){
Comparable<T> temp = t[v];
t[v] = t[w];
t[w] = temp;
}
/**
* 下沉元素,如果元素的子节点大于父节点,就下沉元素
* @param k 元素的索引
*/
public void sink(int k){
while(2*k <= size ){
//获取子节点
int j = 2*k;
//从两个孩子中选出较小那个
if(j+1 <= size && !less(data[j],data[j+1])){
j++;
}
//如果子节点不小于父节点就不交换
if(less(data[k],data[j])){
break;
}
exchange(data, k, j);
k = j;
}
}
/**
* v是否比w小
* @param v
* @param w
* @return
*/
public boolean less (Comparable v,Comparable w){
return v.compareTo(w) < 0 ;
}
/**
* 扩容,交换元素
* @param newSize
*/
@SuppressWarnings("unchecked")
public void swap(int newSize){
if(newSize < size){
return;
}
Comparable<T>[] newItems = new Comparable[newSize];
for(int i=0;i<this.size;i++){
newItems[i+1]=data[i+1];
}
this.data = newItems;
}
}