堆
堆是一棵完全二叉树,如下图,也是一个优先队列。
- 小顶堆:所有节点的左右孩子节点都小于或者等于父节点
- 大顶堆:所有节点的左右孩子节点都大于或者等于父节点
完全二叉树
完全二叉树是除了最下面一层,上面所有层都满的二叉树,并且最下层叶子节点也是从左到右的。
完全二叉树如此有规律,以至于我们选择使用数组表示完全二叉树,而不是必须使用链。
!!!(本文将以数组第1位作为数组第0位进行讲解)
完全二叉树具有的性质
parent表示父节点的数组下标
leftChild表示左孩子节点的数组下标
rightChild表示右孩子节点的数组下标
- leftChild = parent * 2
- rightChild = parent * 2
- parent = leftChild / 2 或者 rightChild / 2
堆的操作
插入操作(小顶堆)
插入时只能从堆的最后插入,也就是往数组中最后一个位置插入数据,例如插入 1 这个节点。
插入 1 这个节点.png
插入 1 之后,此时不满足小顶堆的条件了,我们就需要对堆做出调整,使其重新满足条件。
首先,第一步,用temp记录下新插入节点的值。之后形成一个空穴。
接下来,用temp与父节点(也就是20)进行比较。如果temp大于20,那么好,插入结束了,空穴重新填上1这个值。否则,将20移入空穴。变成下图。 第一次调整.png
接下来,继续重复上述步骤,拿temp与10进行比较,结果发现temp小于10,之后把10移入空穴。如下图。 第二次调整.png
最后,再继续上述方法进行调整。将3移入空穴,因为再往上没有父节点了,所以将temp也就是新节点移入空穴,至此插入过程结束,该过程叫做上滤。 插入结束.png
代码如下
public void insert(T element){
if(currentSize == array.length - 1){
changBiggerArray(array.length * 2 + 1);
}
int hole = ++currentSize;
for( ; (hole>1)&&(compare.compare(array[hole/2], element) > 0); hole/=2){
array[hole] = array[hole/2];
}
array[hole] = element;
}
public void changBiggerArray(int size){
T[] arr1 = (T[])new Object[size];
System.arraycopy(array, 0, arr1, 0, array.length);
array = arr1;
}
插入过程总结:在数组最后插入新数据,也就知道了新节点的下标,之后除以2就知道了父亲节点的下标,之后逐层进行比较,直至父节点小于新插入节点或者没有父节点了。
删除操作(基于上面的小顶堆)
删除操作,其实就是出队操作,将小顶堆顶部的元素弹出。
例如上面的小顶堆,执行一次删除操作后变成:
删除.png
此时在堆顶形成了空穴,为了保持堆的结构,又要对堆进行调整。
调整过程如下
从顶部空穴开始判断左右孩子节点哪个小,选出来较小的,之后与堆的最后一个节点(本堆中的20)比较大小,如果是最后一个节点小,那么将最后一个节点移入到空穴中,结束调整。如果最后一个节点大的话,那么将左右孩子节点中较小的移入空穴。
重复上述的操作
第二次调整.png最后发现空穴左右孩子节点不全或者没有左右孩子节点,那么直接将20移入空穴,结束调整。
代码如下
public T delete(){
T root = array[1];
int current = 1;
int child = 2 * current;
while(currentSize > 2*current){
if((child+1 != currentSize) && (compare.compare(array[child], array[child+1]) > 0)) {
++child;
}
if(compare.compare(array[child], array[currentSize]) < 0){
array[current] = array[child];
current = child;
child = child << 1;
}else{
break;
}
}
array[current] = array[currentSize];
currentSize--;
return root;
}
删除总结:直接将数组第一位移除形成空穴,判断是否左右孩子都齐全,如果不全,则直接将末尾节点移入空穴。之后重复上面的操作(下滤)。
全部代码如下
* Created by ShouJingGuo on 2018/3/15.
* 二叉堆具有的性质:
* 1.是一棵完全二叉树,从数组arr[1]位置开始存储,满足左右孩子的数组下标/2 = 父亲节点的数组下标
* 树高h 含有节点数目大于等于2<<h 小于等于2<<(h+1)-1
* 2.堆顶元素是数组最大值或者最小值,每个节点的子节点都大于等于或者小于等于该节点。
*/
public class BinaryHeap<T> {
private int currentSize = 0;
private T[] array;
private Comparator<T> compare;
BinaryHeap(int size, Comparator<T> compare){
array = (T[])new Object[size];
this.compare = compare;
}
public void insert(T element){
if(currentSize == array.length - 1){
changBiggerArray(array.length * 2 + 1);
}
int hole = ++currentSize;
for( ; (hole>1)&&(compare.compare(array[hole/2], element) > 0); hole/=2){
array[hole] = array[hole/2];
}
array[hole] = element;
}
public void changBiggerArray(int size){
T[] arr1 = (T[])new Object[size];
System.arraycopy(array, 0, arr1, 0, array.length);
array = arr1;
}
public T delete(){
T root = array[1];
int current = 1;
int child = 2 * current;
while(currentSize > 2*current){
if((child+1 != currentSize) && (compare.compare(array[child], array[child+1]) > 0)) {
++child;
}
if(compare.compare(array[child], array[currentSize]) < 0){
array[current] = array[child];
current = child;
child = child << 1;
}else{
break;
}
}
array[current] = array[currentSize];
currentSize--;
return root;
}
public static void main(String[] args) {
BinaryHeap<Integer> binaryHeap = new BinaryHeap<>(8, new IntegerComparator());
binaryHeap.insert(6);
binaryHeap.insert(5);
binaryHeap.insert(9);
binaryHeap.insert(11);
binaryHeap.insert(14);
binaryHeap.insert(10);
binaryHeap.insert(15);
binaryHeap.insert(20);
binaryHeap.insert(31);
binaryHeap.insert(27);
binaryHeap.insert(19);
System.out.println(Arrays.toString(binaryHeap.array));
binaryHeap.delete();
}
}
网友评论