堆是一种特殊的二叉树。
最小值堆:最小值堆的特性。
对于堆的任意非叶节点K,K的值总是小于或者等于左右子节点。
K <= 左节点;K <= 又节点;
堆实例:
堆实际上是一个完全二叉树(若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。)可以用数组表示。
堆中存储是局部有序的。
创建堆:
交换创建堆,交换有2,1)向下交换,2)向上交换。
1)向下交换。
假设根的左右子树都已是堆,并且根元素为R,此时有2种情况。
(1)R的值小于或者等于左右子结点,此时堆完成。
(2)R的值大于其中一个或者两个子女,此时R应该与两个子女中较小的一个交换,结果得到一个新堆,
R到达新位置后继续和其两个子女,如果R小于或者等于其左右子女,建堆完成,否则重复上一过程,直到
R小于或等于其左右子女,或R成为新的叶结点。
堆的数组实现:
public abstract class AbstractHeap<T extends Comparable<T>> implements Heap<T> {
//最大堆大小
private static final int MAX_SIZE = Integer.MAX_VALUE;
//数组大小
protected int size;
//堆中数组个数
protected int currentSize;
//存储堆的数据
protected T[] data;
AbstractHeap(){
this(0,16,null);
}
AbstractHeap(T[] data){
this(data.length,data.length,data);
}
AbstractHeap(int currentSize,int size,T[] data){
this.currentSize = currentSize;
this.size = size;
this.data = data;
if (data != null)
//根据给定数组 创建堆
buildHeap(data);
}
}
用给定的数组创建堆,从堆中第一个分支结点,从下往上网上交换创建堆:
private void buildHeap(T[] t){
if (t.length > 1){
//从下到上第一个分支结点
int pos = t.length / 2 - 1;
for (int i = pos ; i >= 0 ; i -- ){
//向下交换
siftDown(i);
}
}
}
交换函数:
void siftDown(int pos) {
// pos为第一个分支结点位置
int tmp = pos;
// j为其左结点
int j = tmp * 2 + 1;
//保存当前结点值
T tmpNode = data[tmp];
while ( j < currentSize){
//j < currentSize - 1 防止右结点不存在,j+1 = tmp *2 +1 ,data[j+1] 为右结点
if (j < currentSize - 1 && data[j].compareTo(data[j+1]) == 1){
//若右结点值比较小,j++
j++;
}
if (tmpNode.compareTo(data[j]) == 1){
//若当前结点值大于左右结点中最小的值,将最小的哪个结点值上移当前结点,
data[tmp] = data[j];
//tmp 为上移结点下标
tmp = j;
j = j * 2 + 1;
}
//如果当前结点值小于其左右子女中最小的,建堆完成,跳出循环
else break;
//将当前结点值 赋值给上移结点
data[tmp] = tmpNode;
}
}
2)向上交换
在堆中新增值时,采用向上交换:
public boolean insert(T t) {
if (t == null){
return false;
}
if (currentSize == size){
return false;
}
//直接将新增值放到数组的最后一个位置,并向上交换
data[currentSize] = t;
siftUp(currentSize);
currentSize ++;
return true;
}
void siftUp(int pos) {
//当前位置下标
int tmp = pos;
//父结点下标
int j = (pos - 1) / 2;
//当前结点
T tmpNode = data[tmp];
while (j >= 0){
//如果当前结点值小于父结点,当前结点值向上移动,直到根结点或者当前结点值打于根结点值跳出循环
if (tmpNode.compareTo(data[j]) == -1){
data[tmp] = data[j];
tmp = j;
j = j / 2 - 1;
}else break;
tmpNode = data[tmp];
}
}
创建堆可以根据已有数据创建采用向下移动创建,也可以一个一个的插入值,采用向上移动的方法创建堆。
小结:
堆的特性,父结点的值小于左右子女结点值。
堆保持局部有序性,可以用左优先队列。
堆排序时间复杂度为O(n)