优先级队列(堆)

简介: 优先级队列(堆)

一 堆的概念

1 堆在逻辑上是一棵完全二叉树,在物理存储结构采用的是数组

2 满足父亲节点的值大于(小于)左右孩子子节点的值(左右节点大小不必比较),叫做大根堆(小根堆)或者是最大堆(最小堆)

3 堆的基本作用是快速寻找集合中的最值


二 堆的操作(以大堆为例)

2.1 建堆(向下调整)

建堆前:int[] array = { 27,15,19,18,28,34,65,49,25,37 };


**思路:**叶子节点本身可以是视作一个堆的,对于一个根节点而言,只需要满足左右子树是堆就可以了,那么就需要从最后一个非叶子节点开始,依次向下调整,直至到根节点为止,此时的完全二叉树就是一个堆。

代码实现:

public class Heap {
    //堆底层的数组
    public int[] elem;
    //表示数组中实际存放的数据个数
    public int Useside;
    //提供一个构造方法,对数组进行实例化
    public Heap(){
        //以十个数据以内为例
        this.elem = new int[10];
    }

    public void CreatHeap(int[] array){
        //将给定的数据放到elem数组中
        for (int i = 0; i < array.length; i++) {
            elem[i]=array[i];
            Useside++;
        }
        //根据思路,我们必须从最后一个非叶子节点,也就是最后一个父亲节点开始向下调整
        for(int parent = ((Useside-1)-1)/2;parent>=0;parent--){
            shiftDown(parent,Useside);
        }
    }

    //向下调整
    public void shiftDown(int parent,int len){
        int child=2*parent+1;
        //向下调整的过程中,要保证孩子节点所在的位置要小于数组的有效长度,超过的已经是调整好了的
        while(child<len){
            //确保有右节点,且找到左右节点中最大值
            if(child+1<len&&elem[child]<elem[child+1]){
                child++;
            }
            if(elem[child]>elem[parent]){
                //1.交换parent与child的值
                int tmp = elem[child];
                elem[child]=elem[parent];
                elem[parent]=tmp;
                //2 向下调整,检查之后的堆是否还是符合大根堆
                parent=child;
                child=2*parent+1;
            }else{//说明此时的parent所在的堆就是大根堆了
                break;
            }
        }
    }
}

运行结果:

2.2 添加元素(向上调整)

在建堆的基础之上,假设我们需要添加元素80


**思路:**与向下调整相反,我们要找到新加入节点的位置,并且要和其父亲节点进行比较,如果比父亲节点大,那么需要接着向上比较,直到child调整到根节点位置或者在调节的过程中,已经成为了大根堆。

代码实现:

    public void offer(int val){
        //如果满了,我们需要进行扩容处理
        if(isFull()){
            elem=Arrays.copyOf(elem,2*elem.length);
        }
        elem[Useside++]=val;
        //向上调整
        shiftUp(Useside-1);
    }
    //向上调整
    public void shiftUp(int child){
        int parent = (child-1)/2;
        while(parent>=0){
            //由于比父亲节点大,此时需要通过向上调整
            if(elem[child]>elem[parent]){
                int tmp = elem[child];
                elem[child]=elem[parent];
                elem[parent]=tmp;
                //还需要向上调整,进行比较
                child=parent;
                parent=(child-1)/2;
            }else{//说明没有父亲节点大,那么此时还是一个大根堆,不需要向上调整
                break;
            }
        }
    }
    public boolean isFull(){
        return Useside==elem.length;
    }

实现结果:

2.3 删除元素

**思路:**需要先将堆顶元素与堆的最后一个元素进行换位,然后数组的长度减一,最后对根节点这棵树进行向下调整就可以了

代码实现:

 //删除元素
    public int poll(){
        if (isEmpty()){
            throw new RuntimeException("数组中没有数据");
        }
        int oldvalue = elem[0];
        elem[0]=elem[Useside-1];
        elem[Useside-1]=oldvalue;
        Useside--;
        shiftDown(0,Useside);

        return oldvalue;
    }
    public boolean isEmpty(){
        return Useside==0;
    }

实现结果:

2.4 建堆的时间复杂度

从粗略而言,建堆的时间复杂度为O(n*logn),实际上建堆的时间复杂度只需要O(n)具体分析:堆的时间复杂度分析


三 堆的应用——优先级队列(PriorityQueue)

在我们java中,优先级队列的实现就是靠堆来实现的,其实关于优先级队列的底层一些方法,其实就是堆的一些添加与删除的方法


四 课后总结

对于堆的理论其实主要理解向上调整以及向下调整两个问题,其他的都还算是比较简单的,对于堆的理论学习,目前就暂时到这里,下篇我们将利用所学的堆的知识去手撕代码。

目录
相关文章
|
6月前
堆(优先级队列 PriorityQueue)
堆(优先级队列 PriorityQueue)
41 0
|
存储 安全 Java
数据结构优先级队列(堆)
数据结构优先级队列(堆)
82 1
|
2月前
|
前端开发 算法 JavaScript
最小堆最大堆了解吗?一文了解堆在前端中的应用
该文章详细解释了堆数据结构(特别是最小堆)的概念与性质,并提供了使用JavaScript实现最小堆的具体代码示例,包括堆的插入、删除等操作方法。
最小堆最大堆了解吗?一文了解堆在前端中的应用
|
2月前
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
52 5
【数据结构】优先级队列(堆)从实现到应用详解
|
6月前
|
存储 安全
堆与优先级队列
堆与优先级队列
37 0
|
6月前
|
存储 算法 安全
堆 和 优先级队列(超详细讲解,就怕你学不会)
堆 和 优先级队列(超详细讲解,就怕你学不会)
|
6月前
|
存储 算法
什么是堆,优先级队列的模拟实现
什么是堆,优先级队列的模拟实现
42 0
|
存储 算法
优先级队列(堆)&&  堆排序
优先级队列(堆)&&  堆排序
50 0
|
6月前
|
存储 安全 Java
优先级队列(堆)
优先级队列(堆)
|
存储 Java
基于堆的优先级队列
java自带的优先级队列默认是小顶堆,现在来写一个大顶堆的
85 0