优先级队列

概念+例子

队列是一种先进先出的数据结构,但有时操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列。这种情况下,数据结构应该提出两个最基本的操作:一个是返回最高优先级对象,一个是添加新的对象。这种数据结构就是优先级队列(Priority Queue)

JDK:提供PriorityQueue接口使用

Ⅰ. 注意:插入的元素不能为null,且元素之间必须要能够进行比较

Ⅱ.插入||删除→在插入和删除元素期间,优先级队列中的元素会自定进行调整 时间复杂度:O(log2N) 发现:默认情况下,不论怎么调整,0号位置的元素始终是最小的

Ⅲ.底层结构→堆(new出的对象在堆上,优先级队列底层结构也是堆,此处的两个堆不是同一个概念)
new→堆:指的是一块具有特殊作用的内存空间
优先级队列底层的堆:指的是一种数据结构

PriorityQueue中包含的接口

构造方式:PriorityQueue()空的优先级队列→默认的容量:11
PriorityQueue(capacity):指定初始化容量大小→注意:capacity不能小于1,如果小于1,抛异常
PriorityQueue(集合对象):用一个集合中的元素来构造优先级队列
》Offer(x):插入元素X→Olog(N)
》poll()→删除元素,每次都是将最小的元素删除了
》peek()→取最小的元素
》size()→获取有效元素个数
》isEmpty()→检查是否为空
》clear()→将优先级队列中的元素清空

Top-K问题

Top-K问题:前K个最大或者最小的元素
例:给一非空的单词列表,返回前k个出现次数最多的单词
大概思路:1.统计每个单词出现的次数
2.借助优先级队列获取出现次数最多的前K个单词

堆的概念

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆中的元素:将其按照完全二叉树的方式存储在数组中(将完全二叉树按照排序的方式进行存储)
任意节点:比其孩子节点都小→小堆
任意节点:比其孩子节点都大→大堆

在这里插入图片描述
堆的特性:堆顶元素一定是堆中所有元素的最大值(大堆)或者最小值(小堆)
从堆顶到其叶子每条路径都是降序(大堆)或者升序(小堆)→堆序

堆的存储方式

堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储
在这里插入图片描述
注意:对于**非完全二叉树,则不适合顺序的方式进行存储。**因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。

将元素存储到数组中后,可以根据二叉树的性质对树进行还原。假设i为节点在数组中的下标,则有:
Ⅰ.如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
Ⅱ.如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
Ⅲ.如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

例:堆的创建
对于集合{ 27,15,19,18,28,34,65,49,25,37 }中的数据,如果将其创建成堆呢?
在这里插入图片描述
画图后发现:根节点的左右子树已经完全满足堆的性质,因此只需将根节点向下调整好即可

向下过程(以小堆为例):

  1. 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子)
  2. 如果parent的左孩子存在,即:child < size, 进行以下操作,直到parent的左孩子不存在
    .parent右孩子是否存在,存在找到左右孩子中最小的孩子,让child标记
    .将parent与较小的孩子child比较,如果:
    Ⅰ.parent小于较小的孩子child,调整结束
    Ⅱ.否则,交换parent与较小的孩子child,交换完之后,parent中大的元素向下移动,可能导致子树不满足对的性质,因此需要继续向下调整,即parent=child;child=parent*2+1

注意:在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整。
时间复杂度分析:
最坏的情况为从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为O(log2N)

import java.util.PriorityQueue;

public class MyPriorityQueue {
    private int[] array;

    public MyPriorityQueue() {
        //默认的构造----将其底层容量设置为11
        array = new int[11];
    }

    public MyPriorityQueue(int initCapacity) {
        if (initCapacity < 1) {
            //标准库:抛出一个非法参数的异常
            initCapacity = 11;
        }

        array = new int[initCapacity];
    }

    //注意:标准库中没有该接口---标准库中可以采用集合来构造优先级数列
    public MyPriorityQueue(int[] arr) {
        array = new int[arr.length];
        for (int i = 0; i < arr.length; ++i) {
            array[i] = arr[i];
        }

        //将array中的元素调整,让其满足小堆的性质
        //找倒数第一个非叶子节点
        int lastLeaf = (array.length - 2) >> 1;
        for (int root = lastLeaf; root >= 0; root--) {
            shiftdown(root);
        }
    }

    //parent表示本次需要调整的节点的小标
    //调整以parent为根的二叉树
    //注意:调整之前,一定要保证parent的左右子树已经满足小堆的性质
    //如果要检测parent是否满足小堆的性质,只需要使用parent与其孩子进行比较
    //满足小堆性质→说明以parent为根的二叉树已经是小堆
    //不满足小堆性质→说明parent比起孩子大,此时需要将parent与其较小的孩子进行交换
    //交换完以后,parent较大的元素向下移动,可能导致其子树不满足小堆的性质
    //需要继续调整其子树
    private void shiftdown(int parent) {
        //使用child标记parent的较小的孩子
        //默认情况下先让其标记左孩子,因为parent可能有左孩子但是没有右孩子
        int child = parent * 2 + 1;
        int size=array.length;
        while (child < size) {
            //找parent中较小的孩子
            //在用左右孩子比较时,必须要保证右孩子存在---while循环条件已经保证左孩子存在
            if (child + 1 < size && array[child + 1] < array[child]) {
                child += 1;
            }
            //检测较小的孩子是否比双亲小
            if (array[child] < array[parent]) {
                //说明parent不满足小堆的性质--将parent和child交换
                swap(parent, child);
                //parent较大的元素向下移动可能会导致子树不满足堆的性质
                //继续向下调整
                parent = child;
                child = parent * 2 + 1;
            } else {
                //以parent为根的二叉树已经满足堆的性质
                return;
            }
        }
    }

    private void swap(int parent, int child) {
        int temp = array[parent];
        array[parent] = array[child];
        array[child] = temp;
    }

    public static void main(String[] args) {
        int array[] = {5, 3, 7, 1, 4, 6, 2};

        MyPriorityQueue mp = new MyPriorityQueue(array);
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值