数据结构:优先级队列(堆)

一.优先级队列

1.1什么是优先级队列

按照优先级的大小,动态的出队(动态指的是元素个数在动态变化,而非固定)
普通队列:FIFO按照元素的入队顺序出队,先入先出
在这里插入图片描述
在计算机领域,若见到log(n)时间复杂度,近乎一定和“树”结构相关(并非一定要构建一棵树结构,而是算法过程逻辑上是一棵树)
归并,快速递归过程也是一颗递归树,nlogn。

1.2现实生活中优先级队列PriorityQueue

现实生活中优先级队列PriorityQueue
1.医生根据病人病情排手术排期
病情相同的情况下按照先来先排,若病情较重,优先安排手术

和排序有什么区别呢?能否按照病情排序,然后排序后的病人数组安排手术?
排序是数组元素个数确定,不会发生变化。
优先级队列:此时数据动态变化。
2.操作系统的任务调度
优先级队列:系统的任务一般优先级都比普通的应用优先级要高。CPU内存等资源有限,当资源不够用时,优先让优先级较高的应用获取资源
在这里插入图片描述

1.3二叉堆

基于二叉树的堆(二叉堆,应用最广泛的堆)
本节课讲的二叉堆的特点:
1.是一颗完全二叉树,基于数组来存储(元素都是靠左排列,数组中存储时不会浪费空间)
在这里插入图片描述
一般二叉树要存储空节点,浪费大量数组空间。
只有完全二叉树适合使用数组这种结构来存储,不用存储空节点,其他的二叉树都要使用链式结构。
2.关于节点值:
堆中根节点的值>=子树节点中的值(最大堆,大根堆)
堆中根节点的值<=子树节点中的值(最小堆,小根堆)
堆中树根>=子树中所有节点
子树也仍然满足堆得定义
1.必须是完全二叉树
2.从上至下都满足
根>=子树
根<=子树
在这里插入图片描述
在最大堆中是否越高的节点一定就比越低的节点值大呢?
节点的层次和节点的大小没有任何关系,只能保证当前树中,树根是最大值。其他节点多层次大小关系不确定

JDK中的PriorityQueue默认是基于最小堆的实现

1.4关于节点的编号

因为堆是基于数组来 存储的,节点之间的关系通过数组下标来表示,从0开始编号,数组的下标也是从0开始
假设此时节点编号为i,且存在父子节点
父节点的编号parent = (i - 1)/2
左子树编号left = 2i + 1;
右子树编号left = 2
i + 2;
保存到数组
在这里插入图片描述
节点之间通过数组的索引下标来找到父子节点

1.5基于动态数组ArrayList实现的最大堆

堆得基础表示
向堆中添加元素siftUp
堆是数组实现的,添加元素,数组咋添加元素?
此时的这个堆仍然满足完全二叉树的性质。
但是此时这个完全二叉树不再是一个最大堆了,因此需要进行元素的上浮操作,让新添加的元素上浮到合适位置。
新增一个元素52
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
siftUp(int k){
while(k > 0 && data[k] > data[parent(k)]){
swap(k,parent(k));
k = parent(k);
}
}
上浮操作的终止条件:
1.当前已经上浮到树根=》这个元素一定是最大值
2.当前元素<=父节点对应的元素值,此时元素落在了正确位置在这里插入图片描述

1.6取出最大堆

1.最大堆的最大值一定处在树根节点,直接取出树根即可
2.需要融合左右两个子树,使得取出树根后,这棵树仍然是个最大堆,此时融合操作比较复杂,因为左右子树大小关系不定,且节点的大小和层次没有必然联系。

使用最低的成本来融合左右树,使其仍然满足最大堆的性质。
比较和移动最少,删除元素=》移除数组的末尾元素

1.直接取出树根 节点就是当前堆的最大值
2.将堆中最后一个元素顶到对顶,然后进行元素的下沉操作。siftDown=>使其仍然满足最大堆的性质
在这里插入图片描述
16覆盖到树根,然后从树中删除这个节点,移除16之后,这棵树仍然是一个完全二叉树,此时不满足最大堆的性质。不断交换和左右子树的最大值进行交换直到16这个节点落在了最终位置。
在这里插入图片描述
siftDown(int k)=>从索引k开始进行元素的下沉操作
while(leftChild(k)<data.size())=>左子树还存在子树。
{
//1.找到左右子树的最大值交换
//2.继续向下判断。
}
在这里插入图片描述
在这里插入图片描述
就是建立最大堆,然后在最大堆的基础上进行调整操作。就可以得到一个升序集合

1.7heapify-堆化

现在给你一个整形数组,都可以看作是一个完全二叉树,距离最大堆就差一个元素调整操作。
[15,17,19,13,22,16,28,30,41,62]
1.将这n个元素依次调用add方法添加到一个新的最大堆中
遍历原数组,创建一个新的最大堆,调用最大堆的add方法即可。
时间复杂度:nlogn
2.原地heapify->0(n)
从最后一个非叶子节点开始进行元素的siftDown操作
从当前二叉树中最后一个小子树开始调整,陆续不断向前,直到调整到根节点
不断将子树调整为最大堆时,最终走到树根时,左右子树全都满足最大堆,只需要下沉根节点,就能得到最终的最大堆,不断将子问题解决
在这里插入图片描述
在这里插入图片描述

二.比较元素大小关系

2.1在java中比较元素大小关系

在java中比较两个元素相等equals
比较两个自定义对象的大小关系,类覆写Comparable接口,实现ComparableTo方法
若一个类 Student implements Comparable,则这个Student类具备了可比较的能力。
ComparableTo(Object o){
比较当前对象和传入对象的大小关系
1.>0:当前元素“大于”传入对象o
2.<0:当前元素“小于”传入对象o
3.=0:当前元素“等于”传入对象o
}

实现java.util.comparator接口=》比较器

如果需要得到一个升序数组this-o
如果需要得到一个降序数组又得改代码o-this
假设哪一天,想得到一个按照名字排序的数组,又得改ComparableTo方法
在这里插入图片描述
开闭原则:一段程序应该对扩展开放,对修改关闭。
写好的代码别老改,要有新需求,就拓展新代码。
java.util.comparator接口
一个类若实现了这个接口,表示这个类天生就是为别的类的大小关系服务的
studentSec这个类天生就是为Student对象的排序而存在
覆写compare方法(o1,o2)=>int
表示o1和o2的大小关系
在这里插入图片描述
当把Student类的大小关系比较从Student类中“解耦”此时比较策略非常灵活,需要哪种方式,只需要创建一个新的类实现Comparator接口即可,根据此时大小关系的需要传入比较器对象。
策略模式
在这里插入图片描述

三.基于堆得优先级队列应用

3.1基于堆得优先级队列应用

基于堆得优先级队列应用,=》TopK问题
在这里插入图片描述

TopK问题都可以使用优先级队列解决。
取大用小,取小用大
若需要取出前K个最大元素构造最小堆
若需要取出前K个最小元素构造最大堆
时间复杂度为O(nlogk)

在这里插入图片描述
1.若此时队列的元素个数小于K,直接添加到队列中
2.若此时元素个数==K
a.新扫描到的元素val>=堆顶元素,一定大于此时堆中的所有元素,则这个val一定不是我要的结果,直接跳过
b.若此时新扫描到的元素val<堆顶元素,将堆顶元素出队,将新元素val添加到队列中
c.重复上述过程,直到集合被扫描完毕,队列中恰好就保存了前K个最小值
随着堆顶元素不断交换,会把堆顶元素不断变小,最终队列扫描结束,就存放了最小的K个数。

3.2Map的使用

在这里插入图片描述

四.内部类

4.1什么是内部类

所谓的内部类就是一个类嵌套到另一个类的内部的操作。

4.2匿名内部类

接口是不能直接实例化对象,必须由子类进行向上转型为接口实例化
在这里插入图片描述

五.原地堆排序

给你一个任意的数组,如何就在这个数组的基础上进行堆排序,不创建额外的堆空间
[15,19,11,18,17,6,3,8,1,9]
1.任意数组其实就可以看作是一个完全二叉树
就将这个数组调整为最大堆。
2.不断交换堆顶元素=>从最后一个元素的位置,继续进行siftDown,最大值放在最终位置,
直到数组剩下一个未排序的元素为止
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值