堆(Heap)、优先级队列

堆(heap)

什么是堆

堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一颗树(逻辑上)的数组对象(实质上)。并且堆总是满足下列性质:

  • 堆中某个结点的值总是不大于或不小于其父节点的值
  • 堆总是一颗完全二叉树

在这里插入图片描述

将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

那么对应的已知父节点下标parentIndex,则左孩子的下标为2*parentIndex+1,右孩子的下标为2*parentIndex+2

堆的作用是快速找集合中的最值,例如平时写的题目topK即可用堆来写。

重要操作-向下调整

前提:左右子树必须已经是一个堆,才能调整。

还是以小堆为例:
在这里插入图片描述

那么问题来了,问题一:怎么判断index对应的位置是不是叶子结点?

回答:因为它在逻辑上是一颗完全二叉树,如果一个结点没有左孩子,那么它一定没有右孩子,所以需判断2*index+1和size的大小关系,如果前者小于size则一定有,反之没有。

问题二:找到最小的孩子中最小的(一定有左孩子)

在这里插入图片描述

最小的为右孩子得满足有右孩子并且右孩子比较小才可。

代码实现:

public class Heap {
    public static void adjustDown(int[] array,int size,int index){
        int leftIndex = 2*index + 1;
        //1.判断index是否为叶子结点
        while(leftIndex < size) {
            //2.找最小的孩子
            int minIndex = leftIndex;
            int rightIndex = leftIndex + 1;
            if (rightIndex < size && array[rightIndex] < array[leftIndex]) {
                minIndex = rightIndex;
            }

            //3.比较最小孩子的值和index位置的值
            if (array[index] <= array[minIndex]) {
                return;
            }

            //4.交换
            int t = array[index];
            array[index] = array[minIndex];
            array[minIndex] = t;

            //5.把最小的孩子视为index,继续循环
            index = minIndex;
            leftIndex = 2 * index + 1;
        }
    }
}

建堆

在这里插入图片描述

    public static void createHeap(int[] array,int size){
        //找到层序遍历的最后一个节点下标
        int lastIndex = size - 1;
        //找到最后一个节点的父节点的下标
        int lastParentIndex = (lastIndex - 1)/2;
        //从最后一个父节点一直向下调整,直到调整到第一个节点结束
        for(int i = lastParentIndex;i >= 0;i--){
            adjustDown(array,size,i);
        }
    }

堆的应用

优先级队列

优先级队列在Java中的api->(java.util.PriorityQueue implement Queue),它的底层实际上就是用堆来实现的,那么我们现在自己来实现一下。

  • 首先需要一个数组,这里为了简单方便所以使用固定数组大小100,以及size。
  • 查看元素,即查看堆顶元素(数组首元素)。
  • 删除元素,先将堆的最后一个元素(数组最后一个元素)放到首元素位置,再进行向下调整,因为这样它的左子树和右子树都满足堆,所以只需要调整一次即可,即log(n)
  • 插入元素,将需要插入的元素尾插进去数组,然后对它进行向上调整。
public class MyPriorityQueue {
    private Integer[] array;
    private int size;
    public MyPriorityQueue(){
        //简单起见,不考虑扩容
        array = new Integer[100];
        size = 0;
    }

    public Integer element(){
        if(size == 0){
            throw new RuntimeException("空了");
        }
        return array[0];
    }

    public Integer remove(){
        if(size == 0){
            throw new RuntimeException("空了");
        }
        int e = array[0];
        array[0] = array[size - 1];
        size--;
        adjustDown(0);
        return e;
    }

    //log(n)
    public void add(Integer e){
        array[size++] = e;
        adjustUp(size-1);
    }

    public void adjustUp(int index){
        while(index > 0){
            int parentIndex = (index - 1)/2;
            if(array[parentIndex] <= array[index]){
                return;
            }
            int t = array[parentIndex];
            array[parentIndex] = array[index];
            array[index] = t;

            index = parentIndex;
            parentIndex = (index - 1)/2;
        }
    }

    public void adjustDown(int index){
        int leftIndex = index * 2 + 1;
        while(leftIndex < size){
            int minIndex = leftIndex;
            int rightIndex = leftIndex + 1;
            if(rightIndex < size && array[rightIndex] < array[leftIndex]){
                minIndex = rightIndex;
            }

            if(array[minIndex] >= array[index]){
                return;
            }

            int t = array[minIndex];
            array[minIndex] = array[index];
            array[index] = t;

            index = minIndex;
            leftIndex = index * 2 + 1;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值