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

概念

优先级队列是啥? 

队列是一种先进先出 (FIFO) 的数据结构 ,但有些情况下, 操作的数据可能带有优先级,一般出队
列时,可能需要优先级高的元素先出队列。
在这种情况下, 数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象 。这种数据结构就是优先级队列 (Priority Queue)

堆是啥?

优先级队列的底层运用到堆这种数据结构

堆的特点:总是一棵完全二叉树

大根堆:

每一棵树的根结点总是比左右子节点大

小根堆:

每一棵树的根结点的值总是比左右子节点小,不考虑左右子节点谁大谁小

堆的存储

存储方式采用层序遍历的方式把二叉树的元素一一放到数组里面

那数组可不可以存储非完全二叉树呢?答案是可以的,但是会有空间浪费的情况

像上面右边图,4的位置没有存储元素,这就是一种空间浪费

来手搓一个堆

回顾一下二叉树里面性质的第五点

如何将普通数组转换成堆 

把下面数组的元素画成堆

先画成一棵普通的二叉树

画成大根堆

28<37,互换。最左边子树49>25>18,把49变成该树的根结点,最右边的树65>34>19,也进行交换

调整第二层的树,49>15>37,49作为根,而15<18<25,下方的树得把25变成根

最上面一层的树,65>49>27,65做根结点,而27<34,所以34还得作为该子树的根结点

这就是一个大根堆了

总结:

1.从最后一棵子树开始调整

2.在要变换的树里面,从左右孩子里面找到最大的与根结点比较,大了就进行互换

3.如果能够知道子树根结点下标,那么下一棵子树就是当前根结点下标-1

4.一直调整到0下标这个树为止

先写个初步的代码

public class TestHeap {private int[] elem;public int usedSize;//记录当前堆当中有效的数据个数public TestHeap(){this.elem = new int[10];}//存储数组public void initElem(int[] array){for (int i = 0; i < array.length; i++) {elem[i] = array[i];usedSize++;}}

问题

1.最后一棵子树根结点下标是多少

因为i = len-1,所以根结点index = (i-1)/2

    public void createHeap(){//usedSize-1相当于最后一棵树孩子结点的下标i,再-1是为了求父结点for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {siftDown(parent,usedSize);}}

2.每棵子树调整完之后,结束的位置怎么定?也就是我要从哪里开始调整下一棵子树? 

我们采用向下调整的方法(注意,虽然我们是从最后一颗树往根结点方向调整,但是每一棵树的处理我们还是采用从父结点到子节点的调整方法。为什么用不向上调整?后面我会说到。)

找到最后一个元素置为c,其根结点为p

调整完后不知道下面还有没有元素要调整,所以c还得往下走 

此时c的坐标是19 > 10了,所以可以停止了

    private void siftDown(int parent, int len){int child = 2 * parent + 1;while(child < len){//左右孩子比较大小if(elem[child] < elem[child + 1]){child = child + 1;}//走完上面的if,证明child下标一定是左右两个孩子最大值的下标}}

现在问题来了,写到这里会发生数组越界,如果我的child移到9下标这里,那这个if判断elem[child] < elem[child+1] 这里的child+1 = 10 = usedSize,而这棵树根本就没有10这个下标,造成了越界 

修改一下代码

            if(child+1<len && elem[child] < elem[child + 1]){child = child + 1;}

后面就是比较孩子和父结点的代码了

   /*** 向下调整* @param parent* @param len*/private void siftDown(int parent, int len){int child = 2 * parent + 1;while(child < len){//左右孩子比较大小if(child+1<len && elem[child] < elem[child + 1]){child = child + 1;}//走完上面的if,证明child下标一定是左右两个孩子最大值的下标if(elem[child] > elem[parent]){int tmp = elem[child];elem[child] = elem[parent];elem[parent] = tmp;parent = child;child = 2 * parent + 1;}else{break;//不用比不用调了}}}

测试一下,没有问题😊

怎么计算这个堆的时间复杂度?

考虑最坏情况,就是满二叉树的情况

首先明确一点,最后一层结点时不进行调整的,一般是从倒数第二层结点开始调整的

设树的高度是h

T(N) = (h-1)*2^0+(h-2)*2^1+(h-3)*2^2+......+2*2^(h-3)+1*2^(h-2)

怎么求这个等式?采用错位相减

根据等比求和公式

T(n) = 2 ^ h - 1 - h

因为n=2^h-1    --> h = log(n+1)

代进去T(n) = n - log(n+1)

因为log(n+1)的图长这样,n越大越趋于一个常数

所以整个等式占支配地位的还得是n,所以T(N) ≈ n  -->时间复杂度:O(N)


堆的插入

如果插入的数值比较小

如果插入的数值比较大,那就得一层一层进行调整

这种调整叫做向上调整

    public void swap(int i, int j){int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}public void push(int val){if(isFull()){elem = Arrays.copyOf(elem, 2*elem.length);}elem[usedSize] = val;//向上调整siftUp(usedSize);usedSize++;}//判断满不满public boolean isFull(){return usedSize == elem.length;}public void siftUp(int child){int parent = (child - 1) / 2;while(child>0){if(elem[child] > elem[parent]){swap(child,parent);child = parent;parent = (child - 1) / 2;}else{break;}}}

在测试里面把80push进去,没有问题😊

堆的插入的时间复杂度

因为最坏情况插入的元素是最大的,那这个元素最多也就向上调整到根节点的位置,也就是h

复杂度就是O(logN) 

欸那为什么不用向上调整来建堆呢?😐

我们分析一下,拿这棵满二叉树来说,最底层有8个元素,已经占了一半了,网上建堆得每个元素都遍历一遍,时间复杂度太大了

 

 

堆的删除

因为堆的删除一定是删除优先级最高的值,所以一定是删除大根堆的根结点

比如这个,我们要做的就是删除65

第一步:把65(0下标)与28(最后一个元素)进行交换

第二步:向下调整0下标

    public int pop(){if(empty()){throw new EmptyException("数组空了!");}int oldVal = elem[0];swap(0,usedSize-1);usedSize--;siftDown(0,usedSize);return oldVal;}public boolean empty(){return usedSize == 0;}

测试一下,没有问题😊

习题:

选A(可以自己画图,反正就是层序遍历画树)

选C

 

总共比较3次,左边那个15的原本就是小根堆,所以就不用比较

选C

PriorityQueue

Java集合框架提供了PriorityQueue的优先级队列

注意事项:

        PriorityQueue<Student> priorityQueue1 = new PriorityQueue<>();priorityQueue1.offer(new Student("zhangsan",10));priorityQueue1.offer(new Student("lisi",12));

 1.PriorityQueue放入的元素必须能比较大小,否则会报出下面的错误 

2.不能插入null对象,否则会报出下面的错误

        PriorityQueue<Student> priorityQueue1 = new PriorityQueue<>();priorityQueue1.offer(null);

 

3.没有容量限制,可以插入任意多个元素,内部会自动扩容

4.插入和删除都是O(logn) 

5.使用了最小堆的数据结构,所以每次获取的元素都是最小的元素

oj练习

面试题 17.14. 最小K个数 - 力扣(LeetCode)

设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

示例:

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

提示:

  • 0 <= len(arr) <= 100000
  • 0 <= k <= min(100000, len(arr))

方法一:

建立最小堆,把堆顶k个元素输出出来就行了

代码

    public int[] smallestK(int[] arr, int k) {PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();//向上调整 O(logN)for (int i = 0; i < array.length; i++) {priorityQueue.offer(array[i]);}int[] ret = new int[k];//k*logNfor (int i = 0; i < k; i++) {ret[i] = priorityQueue.poll();}return ret;}

虽然通过了,但是时间复杂度有点大 

方法二:

1.建立大根堆,大小为k,比如我们可以拿前三个元素来建一个大根堆

2.从第k+1个元素开始比较,如果比堆顶元素小,则入堆。当前的堆顶元素(较大的)就舍弃掉,因为已经不符合我对前k个最小的元素的要求了

遍历完整个大根堆长这样

问题来了,PriorityQueue是默认采用小根堆的底层,那我们要怎么让它采用大根堆呢

PriorityQueue源码里面的有一个compare函数

这个函数外层是compareTo函数

这两个函数结合一下,把小的放在前面,大的放在后面,所以实现了小根堆的底层

我们可以重写PriorityQueue里面的compare函数,把大的放在前面

class Imp implements Comparator<Integer>{@Overridepublic int compare(Integer o1, Integer o2) {return o2.compareTo(o1);}
}

整个的代码(上面的重写可以扔到匿名内部类里面)

    public static int[] smallestK(int[] array, int k) {int[] ret = new int[k];if(array == null || k <= 0) {return ret;}//匿名内部类PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2.compareTo(o1);}});//1、建立大小为k的大根堆 O(K*logK)for (int i = 0; i < k; i++) {priorityQueue.offer(array[i]);}//2、遍历剩下的元素 (N-K)*logK// (K*logK) + N*logK  - K*logK   =   N*logK  -->时间复杂度for (int i = k; i < array.length; i++) {int top = priorityQueue.peek();//27if(array[i] < top) {priorityQueue.poll();priorityQueue.offer(array[i]);}}//下面这个不能算topK的复杂度 这个地方是整理数据//k*logKfor (int i = 0; i < k; i++) {ret[i] = priorityQueue.poll();}return ret;}

 

别看力扣上面的通过时间,我们要自行分析时间复杂度 


堆排序

把这个数组从小到大排序,需要建立大根堆

再把这棵树放到堆底,这样最大的元素就有序了

再按照大根堆进行排序(已经有序的元素就不管了),把最大元素49放到堆顶,然后再和堆第的15交换

以此类推,设置一个堆底end,每次拿0下标的元素和它交换,交换完end--

    public void heapSort(){int end = usedSize-1;while(end>0){swap(0,end);siftDown(0,end);end--;}}

时间复杂度O(N*logN)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/174405.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp开发小程序 小米手机真机bottom:0无效 底部间隙 设备安全区域处理办法

uniApp自定义导航 CSS设置 bottom:0竟然无效&#xff0c;而iphone和开发模拟器没有问题 height: 150rpx;position: fixed;left: 0;right: 0;bottom: calc(var(--window-bottom,0)); 网上查了各种方法&#xff0c;包括设置bottom:-20啊以及 padding-bottom: constant(safe-are…

Spark On Hive原理和配置

目录 一、Spark On Hive原理 &#xff08;1&#xff09;为什么要让Spark On Hive&#xff1f; 二、MySQL安装配置&#xff08;root用户&#xff09; &#xff08;1&#xff09;安装MySQL &#xff08;2&#xff09;启动MySQL设置开机启动 &#xff08;3&#xff09;修改MySQL…

后悔没早学这份Python神级文档!2023最新入门到进阶核心知识点学习文档!

如今学 Python 的程序员越来越多&#xff0c;甚至不少人会把 Python 当作第一语言来学习。不过尽管 Python 功能强大上手轻松&#xff0c;但并不代表它的学习曲线不陡峭&#xff0c;得来全不费工夫。 当推开 Python 的大门&#xff0c;你会发现 Python 入门简单但精通很难。看…

Realrek 2.5G交换机 8+1万兆光RTL8373-VB-CG方案简介

新一代2.5G交换机方案RTL8373-VB-CG可以提供4中不同形态 a. 52.5G 电口110G光》RTL8373 b. 52.5G 电口110G电》RTL83738261 c. 82.5G 电口110G光》RTL83738224 d.82.5G 电口110G电口》RTL837382248261 1.概述 Realtek RTL8373-CG是一款低功耗、高性能、高度集成的八端口2.5G和一…

(c语言进阶)字符串函数、字符分类函数和字符转换函数

一.求字符串长度 1.strlen() (1)基本概念 头文件&#xff1a;<string.h> (2)易错点&#xff1a;strlen()的返回值为无符号整形 #include<stdio.h> #include<string.h> int main() {const char* str1 "abcdef";const char* str2 "bbb&q…

N-129基于springboot,vue学生宿舍管理系统

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vuevue-element-admin 服务端技术&#xff1a;springboot,mybatis…

css矩形盒子实现虚线流动边框+css实现step连接箭头

由于项目里需要手写步骤条 且实现指定状态边框虚线流动效果&#xff0c;故使用css去绘制步骤条连接箭头和绘制边框流动效果 效果&#xff1a; 1.绘制步骤条连接箭头 <ul class"process-list"><div v-for"(process, index) in processes" :key&qu…

论文阅读——DistilBERT

ArXiv&#xff1a;https://arxiv.org/abs/1910.01108 Train Loss: DistilBERT&#xff1a; DistilBERT具有与BERT相同的一般结构&#xff0c;层数减少2倍&#xff0c;移除token类型嵌入和pooler。从老师那里取一层来初始化学生。 The token-type embeddings and the pooler a…

UEditorPlus v3.6.0 图标补全,精简代码,快捷操作重构,问题修复

UEditor是由百度开发的所见即所得的开源富文本编辑器&#xff0c;基于MIT开源协议&#xff0c;该富文本编辑器帮助不少网站开发者解决富文本编辑器的难点。 UEditorPlus 是有 ModStart 团队基于 UEditor 二次开发的富文本编辑器&#xff0c;主要做了样式的定制&#xff0c;更符…

Wpf 使用 Prism 实战开发Day03

一.实现左侧菜单绑定 效果图: 1.首先需要在项目中创建 mvvm 的架构模式 创建 Models &#xff0c;放置实体类。 实体类需要继承自Prism 框架的 BindableBase&#xff0c;目的是让实体类支持数据的动态变更! 例如: 系统导航菜单实体类 / <summary>/// 系统导航菜单实体类…

CAD需要学c语言嘛?

CAD需要学c语言嘛&#xff1f; AutoCAD 和 C 语言没有关系的。 如果非要说是 AutoCAD 和哪个编程语言有关系&#xff0c;那应该是 VBA, 可以通过 VBA 编程&#xff0c;最近很多小伙伴找我&#xff0c;说想要一些c语言资料&#xff0c;然后我根据自己从业十年经验&#xff0c;熬…

关于安科瑞交流多回路智能电量采集监控装置在某高速项目的实际应用分析-安科瑞 蒋静

1项目背景 河南安阳林州市某高速公路项目是河南省政府主要打造的一项公路建设项目&#xff0c;该项目全长约70公里&#xff0c;起点位于安阳市内&#xff0c;终点位于林州市县。 该项目的建设旨在缓解当地交通压力&#xff0c;提高区域交通运输能力和服务水平&#xff0c;促进…

竞赛 深度学习卷积神经网络垃圾分类系统 - 深度学习 神经网络 图像识别 垃圾分类 算法 小程序

文章目录 0 简介1 背景意义2 数据集3 数据探索4 数据增广(数据集补充)5 垃圾图像分类5.1 迁移学习5.1.1 什么是迁移学习&#xff1f;5.1.2 为什么要迁移学习&#xff1f; 5.2 模型选择5.3 训练环境5.3.1 硬件配置5.3.2 软件配置 5.4 训练过程5.5 模型分类效果(PC端) 6 构建垃圾…

Ubuntu 诞生 19 年

导读2004 年 10 月 20 日&#xff0c;Ubuntu 4.10 正式发布&#xff0c;代号‘Warty Warthog’。 作为 Ubuntu 第一个版本&#xff0c;4.10 问世后立刻受到广大 Linux 用户欢迎。它搭载了当时最新的 GNOME 2.8 桌面环境&#xff0c;以及一系列实用软件&#xff0c;比如 Mozilla…

一带一路10周年:爱创科技加速中国药企国际化征程

“源自中国&#xff0c;属于世界”。 共建“一带一路”倡议提出10周年来&#xff0c;中国与沿线国家经济深度融合&#xff0c;在共商共建共享的基本原则下&#xff0c;“一带一路”形成了国际合作的平台和机制&#xff0c;跨国经济合作已基本形成。 随着“一带一路”合作日益加…

react中的useState和useImmer的用法

文章目录 一、useState1. 更新基本类型数据2. 更新对象3. 更新嵌套对象4. 更新数组5.更新数组对象 二、Immer1. 什么是Immer2. 使用use-immer更新嵌套对象3. 使用useImmer更新数组内部的对象 一、useState react中文官网教程 1. 更新基本类型数据 在函数式组件中&#xff0c…

IPv6+ 3.0关键技术解析与应用实践探索

IPv6作为面向5G和云计算的智能IP技术&#xff0c;其核心是以IPv6技术架构为底座&#xff0c;并基于用户的新兴业务进行创新发展而来的。任何一项技术创新的背后都有一只看不见的推手-用户的需求&#xff0c;也就是用户的业务发展所需&#xff0c;进一步来说是用户的应用系统在驱…

【广州华锐互动】牛顿运动定律VR虚拟教学软件

在科技日新月异的今天&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐渗透到各个领域&#xff0c;为我们带来了前所未有的沉浸式体验。在教育领域&#xff0c;VR技术的应用也日益广泛&#xff0c;尤其是在物理教学中&#xff0c;牛顿运动定律VR虚拟教学软件为学生…

分析Python招聘数据,可视化展示招聘信息详情

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 一. 数据来源分析 明确需求 明确采集网站以及数据内容 数据: 职位信息 网址: https://we.51job.com/pc/search?keywordpython&searchType3&sortType0&am…

Python通过pyecharts对爬虫房地产数据进行数据可视化分析(一)

一、背景 对Python通过代理使用多线程爬取安居客二手房数据&#xff08;二&#xff09;中爬取的房地产数据进行数据分析与可视化展示 我们爬取到的房产数据&#xff0c;主要是武汉二手房的房源信息&#xff0c;主要包括了待售房源的户型、面积、朝向、楼层、建筑年份、小区名称…