1.优先级队列(堆)
1.1概念
队列是一种先进先出的结构,但是有些情况下,操作的数据带有优先级,一般出队列时,可能需要优先级高的元素先出队列。因此优先级队列可以实现两个基本操作,一个是返回最高优先级对象,另一个时添加新的对象。
2.优先级队列的模拟实现
JDK中PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上做了一些调整。
2.1堆的概念
如果有一个 关键码的集合 K = {k0 , k1 , k2 , ... , kn-1} ,把它的所有元素 按完全二叉树的顺序存储方式存储在一 个一维数组中 ,并满足: Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0 , 1 , 2... ,则 称为 小堆 ( 或大堆) 。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
- 堆中某个节点的值总是不大于或者不小于父亲节点的值
- 堆是一颗完全二叉树
2.2堆的创建
2.2.1堆的向下调整
可以利用二叉树的性质,确定双亲结点和左右孩子。
java
import java.util.Arrays;
public class TestHeap {
private int[] elem;
private int usedSize;
public TestHeap() {
this.elem = new int[10];
}
public void initHeap(int[] array){
for (int i = 0; i < array.length; i++) {
elem[i]=array[i];
usedSize++;//将元素放入堆中
}
}
public void createHeap(){
for (int parent=(usedSize-1-1)/2;parent>=0;parent--){//最后一棵子树根结点的位置
shiftDown(parent,usedSize);
}
}
private void shiftDown(int parent, int usedSize) {//向下调整
int child=(2*parent)+1;//左孩子
while(child<usedSize){
if(child+1<usedSize&&elem[child]<elem[child+1]){
child++;//如果右边子树的值更大那么child+1拿右边子树进行交换
}
if(elem[child]>elem[parent]){
swap(child,parent);
parent=child;
child=2*parent+1;
}else{
break;
}
}
}
private void swap(int i, int j) {
int tmp=elem[i];
elem[i]=elem[j];
elem[j]=tmp;
}
public void offer(int val){
if (isFull()){
this.elem= Arrays.copyOf(elem,2*elem.length);
}
this.elem[usedSize]=val;
shiftUp(usedSize);
usedSize++;
}
private void shiftUp(int child) {
int parent=(child-1)/2;//用孩子结点确定双亲结点
while (child>0){
if (elem[child]>elem[parent]){
swap(child,parent);
child=parent;
parent=(child-1)/2;
}else {
break;
}
}
}
public boolean isFull() {
return usedSize==elem.length;
}
public int poll(){
int tmp=elem[0];
swap(0,usedSize-1);//将最后一个元素和堆顶元素交换
usedSize--;
shiftDown(0,usedSize);//重新调整
return tmp;
}
public void heapSort(){
int end=usedSize-1;
while(end>0){
swap(0,end);
shiftDown(0,end);
end--;
}
}
}
3.常用接口
3.1PriorityQueue的特性
java集合框架中提供了PriorityQueue 和PriorityBlockingQueue两种类型的优先级队列,PriorityQueue是线程不安全的,PriorityBlockingQueue是线程安全的。

注意:
- 使用优先级队列必须导入importjava.util.PriorityQueue包
- PriorityQueue中放置的元素必须能够比较大小,否则会抛出ClassCastException 异常
- 不能插入null对象,否则会抛出NullPointerException
- 没有容量限制,会自动扩容。
- 插入和删除元素的时间复杂度为**
**
- 默认情况下都是小堆

(时间复杂度解释)
优先级队列的扩容说明:
- 如果容量小于64时,是按照oldCapacity的2倍方式扩容的
- 如果容量大于等于64,是按照oldCapacity的1.5倍方式扩容的
- 如果容量超过MAX_ARRAY_SIZE,按照MAX_ARRAY_SIZE来进行扩容
3.2PriorityQueue常用接口
1.构造方法


创造一个空的优先级队列,默认容量是11.

创建一个初始容量为initialCapacity的优先级队列,注意:initialCapacity不能小于1,否则会抛IllegalArgumentException异常


注意:默认情况下,PriorityQueue队列是小堆,如果要实现大堆,需要用户自行提供比较器。
java
class IntCmp implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}
3.3TOP-k问题
面试题 17.14. 最小K个数 - 力扣(LeetCode)
java
class IntCmp implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}
class Solution {
public int[] smallestK(int[] arr, int k) {
int ret[]=new int[k];
if(arr==null||k<=0){
return ret;
}
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new IntCmp());
for (int i = 0; i < k; i++) {
priorityQueue.offer(arr[i]);
}
for(int i=k;i<arr.length;i++){
int top=priorityQueue.peek();
if(arr[i]<top){
priorityQueue.poll();
priorityQueue.offer(arr[i]);
}
}
for(int i=0;i<k;i++){
ret[i]=priorityQueue.poll();
}
return ret;
}
}
- 最大堆的堆顶元素是当前堆中最大的数
- 当新元素比堆顶小的时候,替换堆顶,这样最终堆中保留的就是最小的 k 个数
4.堆的应用
4.1堆排序
1.建堆
- 升序:大根堆
- 降序小根堆
2.利用堆删除的思想进行排序,向下调整。
5.对象的比较
5.1覆写基类equals
java
class Student {
public String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
5.2基于Comparble接口类比较
java
public class Card implements Comparable<Card> {
public int rank; // 数值
public String suit; // 花色
public Card(int rank, String suit) {
this.rank = rank;
this.suit = suit;
}
// 根据数值比较,不管花色
// 这里我们认为 null 是最小的
@Override
public int compareTo(Card o) {
if (o == null) {
return 1;
}
return rank - o.rank;
}
public static void main(String[] args){
Card p = new Card(1, "♠");
Card q = new Card(2, "♠");
Card o = new Card(1, "♠");
System.out.println(p.compareTo(o)); // == 0,表示牌相等
System.out.println(p.compareTo(q)); // < 0,表示 p 比较小
System.out.println(q.compareTo(p)); // > 0,表示 q 比较大
}
}
5.3基于比较器的比较
覆写 Comparator 中的 compare 方法
这种方法对类的侵入性比较弱。
java
class IntCmp implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
}