1 优先级队列
概念:队列是一种先进先出的数据结构,操作的数据可能带有优先级,一般出队列时可能需要优先级高的元素先出队列。
数据结构提供两种基本操作:返回最高优先级对象;添加新的对象。
2 优先级队列的模拟实现
PriorityQueue底层使用了堆的数据结构,堆是在完全二叉树的基础上进行了一些元素的调整。
2.1 堆的概念
- 小堆(小根堆):每颗二叉树的根节点都比左右孩子小;
- 大堆(大根堆):每颗二叉树的根节点都比左右孩子大;
2.2 堆的存储方式
堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。
- 已知孩子节点下标,父亲节点下标为(i-1)/2
- 已知父亲节点下标,左右孩子节点下标分别为2*i+1、2*i+2
2.1 创建大根堆代码:
时间复杂度:O(n)
import java.util.Arrays;
public class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem=new int[10];
}
//向下调整
//parent:每次调整的根节点;len:每次的结束位置
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++;
}
//child下标一定是左右孩子最大值的下标
if(elem[child]>elem[parent]){
int tmp=elem[parent];
elem[parent]=elem[child];
elem[child]=tmp;
//交换child和parent的值,使得根节点是最大值
parent=child;
child=2*parent+1;
}else{
break;
}
}
}
//创建大根堆
public void createHeap(int[] array){
for(int i=0;i<array.length;i++){
this.elem[i]=array[i];
this.usedSize++;
}
for (int i=(usedSize-1-1)/2;i>=0;i++){
shiftDown(i,usedSize);
}
}
}
2.2 插入元素到堆中
import java.util.Arrays;
public class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem=new int[10];
}
//插入元素到堆中
//向上调整
public void shiftUp(int child){
int parent=(child-1)/2;
while (parent>=0){
if(elem[child]>elem[parent]){
int tmp=elem[parent];
elem[parent]=elem[child];
elem[child]=tmp;
child=parent;
parent=(child-1)/2;
}else {
break;
}
}
}
public void push(int val){
if(isFull()){
this.elem=Arrays.copyOf(this.elem,2*this.elem.length);//2倍扩容
}
this.elem[this.usedSize]=val;//将新插入的元素,放在最后一个位置
this.usedSize++;//元素多一个,usedSize加1
shiftUp(this.usedSize-1);
}
public boolean isFull(){
return this.usedSize==this.elem.length;
}
}
删除堆顶元素
import java.util.Arrays;
public class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem=new int[10];
}
//删除堆顶元素
public int pop(){
if(isEmpty()){
throw new HeapEmptyException("堆为空");
}
int tmp=elem[0];
elem[0]=elem[this.usedSize-1];
elem[this.usedSize]=tmp;
usedSize--;
shiftDown(0,usedSize);
return tmp;
}
public boolean isEmpty(){
return this.usedSize==0;
}
}
public class HeapEmptyException extends RuntimeException {
public HeapEmptyException(String message) {
super(message);
}
}
3 堆的应用
- 堆排序
import java.util.Arrays;
public class TestHeap {
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem=new int[10];
}
//堆排序
public void heapSort(){
int end=usedSize-1;
while (end>0){
int tmp=elem[end];
elem[end]=elem[0];
elem[0]=tmp;
shiftDown(0,end);
end--;
}
}
}
- Top-k问题
Top-k问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
时间复杂度:N*log K
- PriorityQueue的实现
用堆作为底层结构封装优先级队列。PriorityQueue是线程不安全的。
使用PriorityQueue时要注意:
- 使用时必须导入 PriorityQueue 所在的包 ;
- PriorityQueue 中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出 ClassCastException 异常;
- 不能插入 null 对象,否则会抛出 NullPointerException ;
- 没有容量限制,可以插入任意多个元素,其内部可以自动扩容;
- 插入和删除元素的时间复杂度为 O (log2N);
- PriorityQueue 底层使用了堆数据结构;
- PriorityQueue默认情况下是小堆﹣-﹣即每次获取到的元素都是最小的元素;
import java.util.Comparator;
import java.util.PriorityQueue;
public class TestDemo {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue=new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;//小堆
//return o2-o1;//大堆
}
});
//PriorityQueue默认小堆
priorityQueue.offer(10);
priorityQueue.offer(90);
priorityQueue.offer(3);
priorityQueue.offer(67);
System.out.println(priorityQueue.poll());
}
}