美文网首页Java数据结构
Java数据结构 - 堆

Java数据结构 - 堆

作者: 守敬 | 来源:发表于2018-03-19 09:01 被阅读0次

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

    • 小顶堆:所有节点的左右孩子节点都小于或者等于父节点
    • 大顶堆:所有节点的左右孩子节点都大于或者等于父节点
    完全二叉树.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<<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();
        }
    }
    

    相关文章

      网友评论

        本文标题:Java数据结构 - 堆

        本文链接:https://www.haomeiwen.com/subject/jgbeqftx.html