1 优先队列应用
队列是一种先进先出的数据结构,先放入队列的元素会先出队列。但是有这样一种场景,我们希望出队列顺序不是根据放入队列顺序,而是根据元素本身具有的优先级,例如元素优先级高则先出队列,这时就要使用优先队列。
1.1 应用一
我们设想这样一种发送消息的业务场景:消息具有优先级属性,在同一个队列中优先发送优先级高的消息,优先级定义如下:
// 优先级 P1 > P2 > p3
public enum PriorityEnum {
P1(1, "优先级1"),
P2(2, "优先级2"),
P3(3, "优先级3")
;
}
消息对象定义如下:
public class MyMessage implements Comparable {
/**
* 消息优先级
*/
private int priority;
/**
* 消息内容
*/
private String messge;
public MyMessage(int priority, String message) {
this.priority = priority;
this.messge = message;
}
@Override
public int compareTo(Object obj) {
MyMessage message = (MyMessage) obj;
return this.priority - message.priority;
}
}
java.util.PriorityQueue可以实现上述功能:
public class PriorityQueueTest {
public static void main(String[] args) {
PriorityQueue<MyMessage> messageQueue = new PriorityQueue<MyMessage>();
MyMessage m1 = new MyMessage(PriorityEnum.P1.getCode(), "message1");
MyMessage m2 = new MyMessage(PriorityEnum.P2.getCode(), "message2");
MyMessage m3 = new MyMessage(PriorityEnum.P3.getCode(), "message3");
messageQueue.offer(m3);
messageQueue.offer(m1);
messageQueue.offer(m2);
while (messageQueue.size() > 0) {
System.out.println(messageQueue.poll());
}
}
}
代码执行结果:
send message=MyMessage(priority=1, messge=message1)
send message=MyMessage(priority=2, messge=message2)
send message=MyMessage(priority=3, messge=message3)
PriorityQueueTest消息放入优先队列顺序:
3、1、2
PriorityQueueTest从优先队列获取消息顺序:
1、2、3
1.2 应用二
现在消息优先级进行业务变更:
// 优先级 P3 > P2 > p1
public enum PriorityEnum {
P1(1, "优先级1"),
P2(2, "优先级2"),
P3(3, "优先级3")
;
}
此时预期输出顺序如下:
3、2、1
如果要达到预期输出顺序代码要进行如下变更:
public class MyMessage implements Comparable {
@Override
public int compareTo(Object obj) {
MyMessage message = (MyMessage) obj;
return message.priority - this.priority; // 比较关系变更
}
}
2 二叉堆
在第一章节我们看到PriorityQueue可以实现按照元素优先级顺序进行输出,那么其底层原理是什么呢?答案是二叉堆。
2.1 什么是二叉堆
二叉堆分为大顶堆和小顶堆,我们首先看大顶堆,从下图可以看到整棵树中最大值在根节点,所以称为大顶堆:
我们再看小顶堆,从下图可以看到整棵树中最小值在根节点,所以称为小顶堆:
2.2 怎么存储二叉堆
二叉堆看似复杂,其实用数组就可以表示,我们以大顶堆为例:
第一步声明一个长度为10的数组,因为二叉树总共有10个节点:
int[] array = new int[10]
第二步设置根节点100作为数组第一个元素:
array[0] = 100
第三步设置所有节点至数组相应位置:
leftChildIndex = (parentIndex * 2) + 1
rightChildIndex = (parentIndex * 2) + 2
例如设置90至