1.优先级队列
1.1队列是一种先进先出的数据结构,但有的时候我们所操作的数据有这个优先级,可能需要优先级高的先出列,数据结构提供了俩个基本操作,一个是返回最高优先级对象,一个是添加对象。这种数据结构称为优先级队列。
2.优先级队列的模拟实现
jDk1.8中的priorityQueue底层使用了堆这种数据结构,堆本质上就是对二叉树的一些调整
2.1堆的概念
按完全二叉树的顺序存储方式(层序遍历规则)存储在一个一维数组里,每一个树的根结点最大,称为大根堆,每一个树的根结点最小,称为小根堆。
堆的性质
1.堆的某个节点的值总是不大于或者不小于父节点
2.堆总是一颗完全二叉树
2.2堆的存储方式
堆是一颗完全二叉树,因此可以按照层序的规则,然后顺序的去高效存储
2.3堆的创建
这里我们以创建大根堆为例子
当我们把这个数组创建好同时,传给了elem这个数组的时候,我们就要开始用这个数组的数据去创建大根堆
创建大根堆思路:1.我们要找到最后一个子树的父亲节点
2.当我们调整(向下调整)完最后一个子树后,我们该如何移动到下一个子树(子树的父亲节点下标减1)
3.什么时候调整完毕(结束条件)
说明:我们最后一个子树的父亲节点parent=(usedSize-1-1)/2,当parent等于0的时候就是顶部的了。
当面解决完一个子树移动到另一个子树的问题的时候,我们就得开写每一个子树向下调整的这个方法,我们说一下child<usedSize这个条件,我们的根据我们画图可知道,你在最后一个子树之后的节点,是根据parent,去求child,也就是parent*2+1,child不能大于usedSize
剩下的比较交换很简单,看我们图片的代码就行.
2.4大根堆的一些操作实现
1.在大根堆插入一些数据
说明:插入我们要将插入的数据插到,堆的末尾,也就是你一维数组的末尾,插完之后,我们进行向上操作。很简单看代码就可以理解。
2.删除大根堆(删除大根堆只能删完全二叉树的最上面的根结点,也就是把顶部的结点和最后的那个结点换一下,然后删除末尾那个结点就相当于删除了
说明:交换完成之后,记得向下操作,整成大根堆
3.常用接口的介绍
3.1priorityQueue的特性
java集合框架提供了priorityQueue和priorityBlockingQueue俩种类型的优先级队列,priorityQueue是线程不安全的,priorityBlockingQueue是线程安全的。
关于PriorityQueue的注意:
1.使用时必须导入PriorityQueue所在的包:import java.util.PriorityQueue;
2.PriorityQueue中放置的元素必须是能够比较的,不能放入无法比较的对象,否则会抛出ClassCastException异常(我们这里那一个student只定义类去验证)
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new student())
这里会包异常ClassCastException异常,如果我们想传一个自定义类型的对象,我们需要实现让这个自定义类去实现Comparable接口,同时重写,这个接口里的compareTo方法,这样上面这个代码才不会报错。(为什么要实现Comparable这个接口,因为我们源码向上操作开始有一步强转Comparable<? super E>)
这里我们注意:当this在前是小根堆,在后是大根堆。(我们可以通过看源码得知),if(key.compareTo((E)e) >=0,compareTo我们在自定义类里重写了,逻辑就会不一样。
3.不能插入null对象,否则会抛出NullPointerException
4.没有容量限制,可以插入任意多个元素,其中内部可以自动扩容
需要扩容的时候,如果老的容量小于64就2倍扩容,大于等于64的时候就1.5倍扩容,还有一个是你定义是多少容量就是多少容量。
当我们调用的是这个无参构造函数的时候,我们编译器会给这个数组分配11个容量
5.插入和删除元素的时间复杂度为O(log2 N)
6.priorityQueue底层使用了堆数据结构
7.priorityQueue默认情况下是小堆
自定义情况下this在前为小根堆,在后为大根堆
3.2比较器Comparator
说明:当我们自定义的这个类想根据它的age或者name的长度去比较,为了灵活的比较,我们要利用这个比较器,也就是让我们这个类去实现比较器的接口,重写里面的compare方法。
比较器用的时候,实例化,然后传到PriorityQueue里面,它就会调用参数为构造器的那个构造函数。
3.4三种方式对比
Object.equals 所有类都是继承Object的,所有直接复写就行,不过只能比较相等于否
Comparable.compareTo 需要手动实现接口,请入性比较强,一旦实现,每次用该类都有顺序,属于内部顺序
Comparator.compare 需要实现一个比较器对象,对待比较类的侵入弱,但对代码实现侵入性强
oj题:
top-k问题
最大或最小的前k个数据。比如世界前500强
思路就是:如果我们求前k个最小的,我们就把先把数组里的前k个元素放到大根堆里,然后我们将顶部的top的元素和数组k位置的元素比较大小如果top大,我们就把top,poll出去,然后入k位置的元素,我们的priorityQueue会按默认的小根堆排序。
如果我们求前k个最大的,就建立小根堆,把数组前k个元素放进去,这里注意我们要写比较器去控制,PriorityQueue之后的排序是大根堆,放完之后,我们就比较top和k位置的数据,如果top小于k位置的数据,那么就把top出了,k位置数据入了。
堆的排序
我们这个数组从小到大排的话,我们就要建立一个大根堆,然后把最后一个放到顶部,顶部的放尾部,然后进行向下调整,然后从最后一个的前一个再进行上述操作即可