数据结构基础——优先级队列(堆)

本文介绍了优先级队列的概念及其在实际中的应用,如手术排期和任务调度。详细讲解了基于二叉堆的优先级队列,包括最大堆的实现,如何添加和移除元素,以及堆化过程。此外,讨论了优先级队列在解决TopK问题上的应用,并给出了使用堆排序的方法。
摘要由CSDN通过智能技术生成

一、什么是优先级队列(堆)

普通队列:按照元素的入队顺序出队,先入先出。

优先级队列:按照优先级的大小动态出队(动态指的是元素个数动态变化,而非固定)。

现实生活中的优先级队列:

  1. 医生根据病人病情的情况对手术排期

    病情相同的情况下按来的先后顺序,若病情较重优先安排手术

  2. 操作系统的任务调度

    优先级队列,系统的任务一般优先级都比普通应用高

优先级队列的数据是在动态变化的

时间复杂度对比:

入队 出队
普通的链式队列 O(1) O(n)
优先级队列 O(log n) O(log n)

在计算机领域,若见到log n时间复杂度,近乎一定和“树”结构有关

二、基于二叉树的堆(二叉堆)

堆有很多种(d叉堆、索引堆…),二叉堆是应用最广泛的堆。

1.二叉堆的特点:

  1. 是一棵完全二叉树,基于数组存储(元素都是靠左排列,数组中存储不会浪费空间)

    只有完全二叉树适合使用数组这种结构来存储。其他二叉树都要用链式结构。

在这里插入图片描述

  1. 关于节点值:

    堆中根节点值 >= 子树节点中的值(最大堆、大根堆)

    堆中根节点值 <= 子树节点中的值(最小堆、小根堆)

    JDK中的PriorityQueue默认是给予最小堆的实现

    ①.完全二叉树

    ②.所有子树都满足

    ​ 根 >= 子树 最大堆

    ​ 根 <= 子树 最大堆

  2. 关于节点的编号:

因为堆是基于数组来存储的,节点之间的关系通过数组下标来表示,从0开始编号,数组下标也是从0开始

假设此时节点编号为i,且存在父节点

父节点编号 parent = (i - 1) / 2;

左子树编号 left = 2 * i + 1;

右子树编号 right = 2 * i + 2;

2. 最大堆的实现

向堆中添加元素

  1. 数组添加元素尾插,在数组末尾添加元素——>此时仍是一棵完全二叉树,节点紧密排列;

  2. 添加元素后,可能会破坏最大堆的定义,因此进行元素上浮操作siftUp,直到把当前元素上浮到合适位置。

    /**
     * 上浮操作,让新添加的元素去到它该去的位置
     * @param k
     */
    private void siftUp(int k) {
         
        while (k > 0 && elementData.get(k) > elementData.get(parent(k))) {
         
            swap(k , parent(k));
            k = parent(k);
        }
    }
    

在堆中取出最大值(最大堆)

  1. 当前最大堆的最大值就在树根节点,直接取出就可;

  2. 将堆中最后一个元素顶到堆顶(覆盖掉最大节点),然后进行元素下沉操作siftDown使其仍满足最大堆性质。

    /**
     * 从索引k开始进行下沉操作
     * @param k
     */
    private void siftDown(int k) {
         
        //还存在子树
        while (leftChid(k) < size){
         
            int j = leftChid(k);
            //此时还存在右子树
            if (j + 1 < size && elementData.get(j + 1) > elementData.get(j)) {
         
                //此时右子树的值大于左子树
                j ++;
            }
            //此时j一定对应了左右子树最大值索引
            if (elementData.get(k) >= elementData.get(j)) {
         
                //当前元素大于左右子树最大值,下沉结束,元素k落在了最终位置
                break;
            }else {
         
                swap(k,j);
                k = j;
            }
        }
    }
    

heapify——堆化

任意一个整形数组都可以看做一个完全二叉树,只需要进行元素调整操作。

方法:

  1. 将这n个元素依次调用add方法添加到一个新的最大堆中

    时间复杂度:O(n logn )

    空间复杂度:O(n)

  2. 原地堆化 ——> O(n)级别时间复杂度

    从最后一个非叶子节点开始进行元素siftDown操作。从当前二叉树中最后一个小子树开始调整,不断向前,直到调整到根节点

    public MaxHeap(int[] arr) {
         
        elementData = new ArrayList<>(arr.length);
        //1.先将所有元素复制到data数组中
        for (int i : arr) {
         
            elementData.add
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值