Java数据结构 - 堆

堆是一棵完全二叉树,如下图,也是一个优先队列

  • 小顶堆:所有节点的左右孩子节点都小于或者等于父节点
  • 大顶堆:所有节点的左右孩子节点都大于或者等于父节点
完全二叉树.png

完全二叉树

完全二叉树是除了最下面一层,上面所有层都满的二叉树,并且最下层叶子节点也是从左到右的。
完全二叉树如此有规律,以至于我们选择使用数组表示完全二叉树,而不是必须使用链。

!!!(本文将以数组第1位作为数组第0位进行讲解)

完全二叉树具有的性质

parent表示父节点的数组下标
leftChild表示左孩子节点的数组下标
rightChild表示右孩子节点的数组下标
  • leftChild = parent * 2
  • rightChild = parent * 2
  • parent = leftChild / 2 或者 rightChild / 2

堆的操作

插入操作(小顶堆)

插入时只能从堆的最后插入,也就是往数组中最后一个位置插入数据,例如插入 1 这个节点。


插入 1 这个节点.png

插入 1 之后,此时不满足小顶堆的条件了,我们就需要对堆做出调整,使其重新满足条件。
首先,第一步,用temp记录下新插入节点的值。之后形成一个空穴。

假装把 1 删除了.png

接下来,用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

重复上述的操作

第二次调整.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< {
    private int currentSize = 0;

    private T[] array;

    private Comparator compare;

    BinaryHeap(int size, Comparator 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 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();
    }
}

你可能感兴趣的:(Java数据结构 - 堆)