Java实现简单的大顶堆

今天刷LeetCode的347. 前 K 个高频元素的时候使用到了优先队列,由于对大顶堆掌握不算熟练,于是写了一个简单大顶堆练手:
实现代码在最后

之前很少使用泛型来写代码,因此实现大顶堆的时候用到了泛型
public class MyMaxHeap<E>

选择采用数组来模拟大顶堆,则类中的私有属性有:

    //使用数组模拟完全二叉树
    private Object [] data;
    //数组的当前大小
    private int size;
    //数组的容量
    private int capacity;
    //比较器,比较器使用超类型限定符
    private Comparator<? super E> comparator;

这里值得注意的是比较器的通配符类型使用的是超类型限定符:<? super E>

为什么使用超类型限定符?
由于我们要用到比较器的compare()方法,而该方法的两个参数应当是E或者E的超类,因此选择使用<? super E>.
关于类型通配符的详细介绍可看:
https://www.zhihu.com/question/20400700/answer/117464182

构造函数
构造函数只写了一个需要比较器的构造函数

 //带有比较器的构造函数
public MyMaxHeap(E[] data,Comparator<? super E> comparator){
    this.data = data;
    this.size=data.length;
    this.capacity=size*2;
    this.comparator=comparator;
    //在利用已有数组进行构造的过程中要重新堆数组进行排列
    //具体就是从最后一个节点的父节点向下筛选构建最大堆,一直到该节点前面的所有节点处理完毕
    int temp=size-1;
    while(temp>0) {
        shiftUp(temp);
        temp--;
    }
}

向下构建最大堆的方法
向下构建最大堆就是不断交换该节点与该节点的最大的孩子节点,一直到没有孩子节点

public void shiftDown(int index){
	//如果当前节点存在孩子节点
        while(getLeftChildren(index)<size&&getLeftChildren(index)!=-1){
            //获取当前节点的最大的孩子节点
            int children=comparator.compare(
                    (E)data[getLeftChildren(index)],
                    (E)data[getRightChildren(index)]
            )>0?getLeftChildren(index):getRightChildren(index);

            //交换当前节点与当前节点的孩子节点
            swap(data,index,children);

            index=children;
        }
    }

向堆中插入数据
插入数据时如果需要扩容,这里只进行了1.5倍的简单扩容
插入数据的时候需要从插入数据的地方向上构建最大堆

public void insert (E num){
        if(capacity<size){
            //简单的扩容机制
            capacity=capacity+capacity>>1;
            data=Arrays.copyOf(data,capacity);
        }
        data[size]=num;
        shiftUp(size);
        size++;
    }

向上构建最大堆

//将下标为index的数组元素向上移动
    public void shiftUp(int index){
        //使用Comparator进行比较当前的结点的数据与该节点的父节点的数据
        while(index>0&&comparator.compare(
                (E)data[index],(E)data[getParentIndex(index)])>0){
            //如果父节点比当前节点小则交换位置
            swap(data,index,getParentIndex(index));
            index=getParentIndex(index);
            System.out.println(index);
        }
    }

取出堆顶部数据
取出顶部数据之后将堆所在数组中最后一个元素放到堆顶,然后向下筛

 public E pollMax(){
        E res=(E)data[0];
        data[0]=data[--size];
        shiftDown(0);
        return res;
    }

附上源码:
由于作者只是为了学习大顶堆,因此源码可能会有一定的纰漏,多多包涵

package com.edu.linear;

import org.junit.Test;

import java.util.*;

/**
 * @author Sepnine
 * @version 1.0
 * @description: EODO
 * @date 2022/10/2 8:26
 */
public class MyMaxHeap<E> {
    //使用数组模拟完全二叉树
    //使用Object数组是因为使用E[]作为类型的话无法进行初始化
    //使用Object数组,然后在取出的时候再做强制类型转换
    private Object [] data;
    //数组的当前大小
    private int size;
    //数组的容量
    private int capacity;

    //比较器,比较器使用超类型限定符
    //该超类型限定符表示:泛型类型Comparator中只能使用E的超类
    //超类型限定符可以作为方法的参数使用,但是不能作为方法的返回值
    //子类型限定符可以作为方法的返回值,但是不能作为方法的参数
    private Comparator<? super E> comparator;

    //构造函数
    public MyMaxHeap(){
        this.size=0;
        this.data=new Object[20];
        capacity=20;
    }
    public MyMaxHeap(E[] data) {
        this.data = data;
        this.size=data.length;
        this.capacity=size*2;
        int temp=size-1;
        while(temp>0) {
            shiftUp(temp);
        }
    }

    //带有比较器的构造函数
    public MyMaxHeap(E[] data,Comparator<? super E> comparator){
        this.data = data;
        this.size=data.length;
        this.capacity=size*2;
        this.comparator=comparator;
        //在利用已有数组进行构造的过程中要重新堆数组进行排列
        //具体就是从最后一个节点的父节点向下筛选构建最大堆,一直到该节点前面的所有节点处理完毕
        int temp=(size-1)/2;
        while(temp>=0) {
            shiftDown(temp);
            temp--;
        }
    }

    /**
     * @description: 检索最大堆的顶部
     * @author Sepnine
     * @date: 2022/10/2 11:29
     */
    public E peekMax(){
        return (E)data[0];
    }

    /**
     * @description: 检索并删除最大堆的顶部
     * @author Sepnine
     * @date: 2022/10/2 11:29
     */
    public E pollMax(){
        E res=(E)data[0];
        data[0]=data[--size];
        shiftDown(0);
        return res;
    }

    /**
     * @description: 从堆的最后插入元素
     * @author Sepnine
     * @date: 2022/10/2 11:28
     */
    public void insert (E num){
        if(capacity<size){
            //简单的扩容机制
            capacity=capacity+capacity>>1;
            data=Arrays.copyOf(data,capacity);
        }
        data[size]=num;
        shiftUp(size);
        size++;
    }

    /** 
     * @description: 从指定下标向上筛(插入元素时使用)
     * @author Sepnine
     * @date: 2022/10/2 11:27
     */ 
    @SuppressWarnings("unchecked")
    //将下标为index的数组元素向上移动
    public void shiftUp(int index){
        //使用Comparator进行比较当前的结点的数据与该节点的父节点的数据
        while(index>0&&comparator.compare(
                (E)data[index],(E)data[getParentIndex(index)])>0){
            //如果父节点比当前节点小则交换位置
            swap(data,index,getParentIndex(index));
            index=getParentIndex(index);
            System.out.println(index);
        }
    }

    /**
     * @description: 从指定下标向下筛(删除元素时使用)
     * @author Sepnine
     * @date: 2022/10/2 11:32
     */
    public void shiftDown(int index){
        while(getLeftChildren(index)<size&&getLeftChildren(index)!=-1){
            //获取当前节点的最大的孩子节点
            int children=comparator.compare(
                    (E)data[getLeftChildren(index)],
                    (E)data[getRightChildren(index)]
            )>0?getLeftChildren(index):getRightChildren(index);

            //交换当前节点与当前节点的孩子节点
            swap(data,index,children);

            index=children;
        }
    }

    /**
     * @description: 获取父节点索引
     * @author Sepnine
     * @date: 2022/10/2 8:41
     */
    public int getParentIndex(int index){
        if(index==0){
            return -1;
        }
        return (index-1)/2;
    }

    //获取左孩子的索引
    public int getLeftChildren(int parent){
        return parent*2+1;
    }

    //获取右孩子的索引
    public int getRightChildren(int parent){
        return parent*2+2;
    }

    //交换数组元素
    public void swap(Object[] data,int tmp1,int tmp2){
        Object tmp=data[tmp1];
        data[tmp1]=data[tmp2];
        data[tmp2]=tmp;
    }

    @Override
    public String toString() {
        StringBuilder res=new StringBuilder();
        res.append("MyMaxHeap:{");
        for(int i=0;i<size;i++){
            res.append((E)data[i]).append(",");
        }
        res.append("}");
        return res.toString();
    }


    public static void main(String[] args){
        Integer [] tmp=new Integer[]{0,12,3,24,124,22,89};
        MyMaxHeap<Integer> myMaxHeap=new MyMaxHeap<>(tmp, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2;
            }
        });
        System.out.println(myMaxHeap);

        System.out.println(myMaxHeap.peekMax());
        System.out.println(myMaxHeap);
        System.out.println(myMaxHeap.pollMax());
        System.out.println(myMaxHeap);
    }
}

posted on 2022-11-11 08:29  Miaobu  阅读(296)  评论(0)    收藏  举报