漫画:什么是优先队列?

简介: 队列的特点是什么?聪明的小伙伴们都知道,是先进先出(FIFO)。那么,优先队列又是什么样子呢?优先队列不再遵循先入先出的原则,而是分为两种情况:最大优先队列,无论入队顺序,当前最大的元素优先出队。最小优先队列,无论入队顺序,当前最小的元素优先出队。

在之前的漫画中,我们介绍了二叉堆和堆排序。没看过的小伙伴可以看一看前文:


漫画:什么是二叉堆?(修正版)


漫画:什么是堆排序?


这一次,我们来讲一讲二叉堆的另外一个应用:优先队列


image.png

image.png


队列的特点是什么?


聪明的小伙伴们都知道,是先进先出(FIFO)


入队列:

image.png


出队列:

image.png


那么,优先队列又是什么样子呢?


优先队列不再遵循先入先出的原则,而是分为两种情况:


最大优先队列,无论入队顺序,当前最大的元素优先出队。

最小优先队列,无论入队顺序,当前最小的元素优先出队。


比如有一个最大优先队列,它的最大元素是8,那么虽然元素8并不是队首元素,但出队的时候仍然让元素8首先出队:

image.png


要满足以上需求,利用线性数据结构并非不能实现,但是时间复杂度较高,最坏时间复杂度O(n),并不是最理想的方式。


至于为什么最坏时间复杂度是O(n),大家可以思考下。


image.png

image.png


让我们回顾一下二叉堆的特性:


1.最大堆的堆顶是整个堆中的最大元素

2.最小堆的堆顶是整个堆中的最小元素


因此,我们可以用最大堆来实现最大优先队列,每一次入队操作就是堆的插入操作,每一次出队操作就是删除堆顶节点。


入队操作:


1.插入新节点5

image.png



2.新节点5上浮到合适位置。

image.png


出队操作:


1.把原堆顶节点10“出队”

image.png



2.最后一个节点1替换到堆顶位置


image.png



3.节点1下沉,节点9成为新堆顶


image.png


image.png

image.png


image.png


public class PriorityQueue {

  1. private int[] array;
  2. private int size;

  3. public PriorityQueue(){
  4.    //队列初始长度32
  5.    array = new int[32];
  6. }

  7. /**
  8. * 入队
  9. * @param key  入队元素
  10. */
  11. private void enQueue(int key) {
  12.    //队列长度超出范围,扩容
  13.    if(size >= array.length){
  14.        resize();
  15.    }
  16.    array[size++] = key;
  17.    upAdjust();
  18. }

  19. /**
  20. * 出队
  21. */
  22. private int deQueue() throws Exception {
  23.    if(size <= 0){
  24.        throw new Exception("the queue is empty !");
  25.    }
  26.    //获取堆顶元素
  27.    int head = array[0];
  28.    //最后一个元素移动到堆顶
  29.    array[0] = array[--size];
  30.    downAdjust();
  31.    return head;
  32. }

  33. /**
  34. * 上浮调整
  35. */
  36. private void upAdjust() {
  37.    int childIndex = size-1;
  38.    int parentIndex = childIndex/2;
  39.    // temp保存插入的叶子节点值,用于最后的赋值
  40.    int temp = array[childIndex];
  41.    while (childIndex > 0 && temp > array[parentIndex])
  42.    {
  43.        //无需真正交换,单向赋值即可
  44.        array[childIndex] = array[parentIndex];
  45.        childIndex = parentIndex;
  46.        parentIndex = parentIndex / 2;
  47.    }
  48.    array[childIndex] = temp;
  49. }

  50. /**
  51. * 下沉调整
  52. */
  53. private void downAdjust() {
  54.    // temp保存父节点值,用于最后的赋值
  55.    int parentIndex = 0;
  56.    int temp = array[parentIndex];
  57.    int childIndex = 1;
  58.    while (childIndex < size) {
  59.        // 如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
  60.        if (childIndex + 1 < size && array[childIndex + 1] > array[childIndex]) {
  61.            childIndex++;
  62.        }
  63.        // 如果父节点大于任何一个孩子的值,直接跳出
  64.        if (temp >= array[childIndex])
  65.            break;
  66.        //无需真正交换,单向赋值即可
  67.        array[parentIndex] = array[childIndex];
  68.        parentIndex = childIndex;
  69.        childIndex = 2 * childIndex + 1;
  70.    }
  71.    array[parentIndex] = temp;
  72. }

  73. /**
  74. * 下沉调整
  75. */
  76. private void resize() {
  77.    //队列容量翻倍
  78.    int newSize = this.size * 2;
  79.    this.array = Arrays.copyOf(this.array, newSize);
  80. }

  81. public static void main(String[] args) throws Exception {
  82.    PriorityQueue priorityQueue = new PriorityQueue();
  83.    priorityQueue.enQueue(3);
  84.    priorityQueue.enQueue(5);
  85.    priorityQueue.enQueue(10);
  86.    priorityQueue.enQueue(2);
  87.    priorityQueue.enQueue(7);
  88.    System.out.println("出队元素:" + priorityQueue.deQueue());
  89.    System.out.println("出队元素:" + priorityQueue.deQueue());
  90. }

}


代码中采用数组来存储二叉堆的元素,因此当元素超过数组范围的时候,需要进行resize来扩大数组长度。

image.png


—————END—————


相关文章
|
2月前
手撸优先队列——二叉堆
队列在生活中常见,如买早点排队。但有时需要优先处理某些元素,如老幼病残孕优先上车,或打印机优先处理单页请求。这种情况下,使用优先队列更为合理。优先队列的基本操作包括入队和出队,常见的实现方法是二叉堆。二叉堆是一种完全二叉树,可以用数组表示,支持高效插入和删除操作。插入时使用上滤,删除时使用下滤,确保堆序性质。构建二叉堆时,从倒数第二层节点开始下滤,直至根节点。
26 3
|
6月前
|
算法 搜索推荐 数据可视化
【漫画算法】插入排序:插入宝石的传说
【漫画算法】插入排序:插入宝石的传说
|
存储
剑指offer 36. 复杂链表的复刻
剑指offer 36. 复杂链表的复刻
49 0
|
存储 NoSQL 关系型数据库
漫画:什么是跳跃表?
如果是没有商品名称的全量查询怎么办?总不可能把数据库里的所有记录全查出来吧,而且还要支持不同字段的排序。 所以,只能提前在内存中存储有序的全量商品集合,每一种排序方式都保存成独立的集合,每次请求的时候按照请求的排序种类,返回对应的集合。
142 1
漫画:什么是跳跃表?
|
搜索推荐 算法
齐姐漫画:排序算法(一)
借用《算法导论》里的例子,就是我们打牌的时候,每新拿一张牌都会把它按顺序插入,这,其实就是插入排序。
152 0
齐姐漫画:排序算法(一)
|
索引
漫画:什么是 “跳表” ?
如何进行二分查找呢? 首先根据数组下标,定位到数组的中间元素:由于要查找的元素20,大于中间元素12,再次定位到数组右半部分的中间元素:这一次定位到的元素正好是20,查找成功。
219 0
漫画:什么是 “跳表” ?
|
存储 算法
漫画:什么是堆排序?
在上一篇漫画中,小灰介绍了 二叉堆 这样一种强大的数据结构: 漫画:什么是二叉堆?(修正版) 那么,这个二叉堆怎样来使用呢?我们这一期将会详细讲述。
223 0
漫画:什么是堆排序?
|
算法
漫画:什么是二分查找?(修订版)
如果我们把场景转换成最初的面试问题:在包含1000个整型元素的有序数组中查找某个特定整数,又该如何去做呢?
123 0
漫画:什么是二分查找?(修订版)
漫画:什么是红黑树?
二叉查找树(BST)具备什么特性呢?
139 0
漫画:什么是红黑树?
漫画:什么是插入排序?
人们如何进行扑克牌的排序呢? 举个例子,比如我手中有红桃6,7,9,10这四张牌,已经处于升序排列:这时候,我又抓到了一张红桃8,如何让手中的五张牌重新变成升序呢?用冒泡排序,选择排序,亦或是快速排序?
169 0
漫画:什么是插入排序?