【数据结构】堆(创建,调整,插入,删除,运用)

目录

堆的概念:

堆的性质:

堆的存储方式:

堆的创建 : 

堆的调整:

向下调整:

向上调整:

堆的创建:

建堆的时间复杂度:

 向下调整:

向上调整:

堆的插入与删除:

 堆的插入:

堆的删除:

堆的应用:

1.PriorityQueue的实现

2.堆排序:

3.Top-k问题 

结语:


堆的概念:

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一 个一维数组中,并满足:Ki = K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

(1)堆中某个节点的值总是不大于(大根堆)或不小于(小根堆)其父节点的值。

(2)堆总是一棵完全二叉树。

具体如下图。 

 

堆的存储方式:

 从堆的概念可知,堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。

注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。

将元素存储到数组中后,可以根据二叉树章节的性质对树进行还原。假设i为节点在数组中的下标,则有:

(1)如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2.

(2)如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子。

(3)如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子。

堆的创建 : 

为了使文章可读性更高下面给出测试堆类的基本代码,下面文章给出的代码均围绕这上面来。

elem:是一个类里面的数组(方便后序操作)

usedSize:是记录数组里面有多少个元素(不是数组容量)

TestHeap:构造方法为了简便把数组容量设为10,大小可以自己调整

initElem:初始化

public class TestHeap {public int[] elem;public int usedSize;public TestHeap(){elem = new int[10];}public void initElem(int[] array){for(int i = 0;i < array.length;i++){elem[i] = array[i];usedSize++;}}
}

堆的调整:

堆有向上调整向下调整两种调整方式,在创建时我们采用向下调整方式,这样的时间复杂度比较低。故下面主要讲解向下过程(以大堆为例) 步骤如下:

向下调整:

1. 让parent标记需要调整的节点,child标记parent的左孩子(注意:parent如果有孩子一定先是有左孩子)。

2. 如果parent的左孩子存在,即:child < size, 进行以下操作,直到parent的左孩子不存在。

(1)parent右孩子是否存在,存在找到左右孩子中最小的孩子,让child进行标记。

(2)将parent与较小的孩子child比较,如果:

a:parent小于较小的孩子child,调整结束。

b:否则:交换parent与较小的孩子child,交换完成之后,parent中大的元素向下移动,可能导致子 树不满足对的性质,因此需要继续向下调整,即parent = child;child = parent*2+1; 然后继续。

 以数组{ 27,15,19,18,28,34,65,49,25,37 }为例调整完变成。

对应的代码如下:

如果想要转换成小堆的话把大于号小于号改一改即可,故下面不在过多描述。

private void siftDown1(int parent,int end){int child = parent*2+1;while(child < end){if(child+1 < usedSize && elem[child] < elem[child+1]){child++;}if(elem[child] > elem[parent]){swap(child,parent);parent = child;child = parent*2+1;}else{break;}}}

 为了使代码整洁故再实现一个swap方法。

private void swap(int i,int j){int tmp = elem[i];elem[i] = elem[j];elem[j] = tmp;}

注意:在调整以parent为根的二叉树时,必须要满足parent的左子树和右子树已经是堆了才可以向下调整。 时间复杂度分析:最坏的情况即图示的情况,从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为O(logn)log下标是以2为底的。

向上调整:

具体过程如图(按照插入的80走的路径):

代码如下:

先找新结点的parent的下标再child大于0的情况下循环进行比较交换,一旦发现不满足条件的立刻跳出循环,因为在使用向上调整之前堆已经排序好了。

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;}}}

堆的创建:

如图所示是从最后一个结点的父亲结点开始遍历每一个结点都调用siftdown1进行向下调整,之后不断减减直到小于0(下标)。

代码如下:

 public void createBigHeap(){for(int parent = (usedSize-1-1)/2;parent >= 0;parent--){siftDown1(parent,usedSize);}}

运行结果:

通过观察elem的元素我们可以发现向下调整成功。💖

建堆的时间复杂度:

 向下调整:

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是 近似值,多几个节点不影响最终结果):如图:

向上调整:

如图:

经过比较我们选择时间复杂度较低的来进行堆的创建即向下调整,至于向上调整我们用于堆的堆的插入与删除。

堆的插入与删除:

 堆的插入:

堆的插入总共需要两个步骤:

(1)先将元素放入到底层空间中(注意:空间不够时需要扩容).

(2)将最后新插入的节点向上调整,直到满足堆的性质.

代码如下:

isFull用来判断是否需要扩容

public boolean isFull(){return usedSize == elem.length;}

siftUp用来向上调整,先找到新参数的parent结点, 传入siftup的参数为新offer进来的参数。

 public void offer(int val){//1.判断满if(isFull()){this.elem = Arrays.copyOf(elem,elem.length*2);}elem[usedSize] = val;usedSize++;siftUp(usedSize-1);}

运行结果如下:

我们可以发现成功增加数据并完成排序。

堆的删除:

注意:堆的删除一定删除的是堆顶元素。具体如下:

(1) 将堆顶元素对堆中最后一个元素交换。

(2) 将堆中有效数据个数减少一个。

(3)对堆顶元素进行向下调整 。

 代码如下:

其实就是把最后一个元素的空间腾出来。

public int poll(){int tmp = elem[0];swap(0,usedSize-1);usedSize--;siftDown1(0,usedSize);return tmp;}

运行结果如下:

可以看到65被成功删除并且数组的序列没有改变

堆的应用:

1.PriorityQueue的实现

可以使用堆来实现优先队列,由于java语言有自带的优先队列故这里不在实现给出它的常用方法直接调用即可。

函数名功能介绍
boolean offer(E e)插入元素e,插入成功返回true,如果e对象为空,抛出NullPointerException异常,注意:空间不够时候会进行扩容
E peek()获取优先级最高的元素,如果优先级队列为空,返回null
E poll()移除优先级最高的元素并返回,如果优先级队列为空,返回null
int size()获取有效元素的个数
void clear()清空
boolean isEmpty()检测优先级队列是否为空,空返回true

2.堆排序:

堆排序即利用堆的思想来进行排序,总共分为两个步骤:

1. 建堆

升序:建大堆

降序:建小堆

2. 利用堆删除思想来进行排序

建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

具体过程如下:

代码后序会将8大排序整理成一篇排序专项。

3.Top-k问题 

TOP-K问题:即求数据集合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都 不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

1. 用数据集合中前K个元素来建堆

(1)前k个最大的元素,则建小堆.

(2)前k个最小的元素,则建大堆。

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

不要用Arrays.sort来做这道题,因为这是一道面试题,用sort就可以快速结束面试,回家等通知了。

top-k问题

使用堆的AC优化代码:

class Imp implements Comparator<Integer>{@Overridepublic int compare(Integer o1,Integer o2){return o2.compareTo(o1);}}
class Solution {public int[] smallestK(int[] arr, int k) {Imp imp = new Imp();PriorityQueue<Integer> priorityqueue = new PriorityQueue<>(imp);int[] ans = new int[k];if(k == 0){return ans;}for(int i = 0;i < k; i++){priorityqueue.offer(arr[i]);}for(int i = k;i < arr.length; i++){int tmp = priorityqueue.peek();if(arr[i] < tmp){priorityqueue.poll();priorityqueue.offer(arr[i]);}}for(int i = 0;i < k; i++){ans[i] = priorityqueue.poll();}return ans;}
}

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

 

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

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

相关文章

2023年06月CCF-GESP编程能力等级认证C++编程二级真题解析

一、单选题(每题2分,共30分) 第1题 高级语言编写的程序需要经过以下()操作,可以生成在计算机上运行的可执行代码。 A. 编辑 B. 保存 C. 调试 D. 编译 答案:D 第2题 能够实现下面流程图功能的伪代码是( )。 A. if 条件判断 then 语句块 B. if 条件判断 then 什么…

vue项目打包部署到flask等后端服务里面,实现前后端不分离部署,解决空白页面和刷新页面not fount问题

1. 编译模式一定要设置为esnext&#xff0c;否则会报错&#xff1a; Strict MIME type checking is enforced for module scripts per HTML spec.Expected a JavaScript module script but the server responded with a MIME type of "text/plain". 具体解释可以看vi…

Leetcode 第 382 场周赛题解

Leetcode 第 382 场周赛题解 Leetcode 第 382 场周赛题解题目1&#xff1a;3019. 按键变更的次数思路代码复杂度分析 题目2&#xff1a;3020. 子集中元素的最大数量思路代码复杂度分析 题目3&#xff1a;3021. Alice 和 Bob 玩鲜花游戏思路代码复杂度分析 题目4&#xff1a;302…

停车场|基于Springboot的停车场管理系统设计与实现(源码+数据库+文档)

停车场管理系统目录 目录 基于Springboot的停车场管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、管理员功能实现 &#xff08;1&#xff09;车位管理 &#xff08;2&#xff09;车位预订管理 &#xff08;3&#xff09;公告管理 &#xff08;4&#…

Vulnhub-Empire靶机-详细打靶流程

渗透思路 1.确认靶机IP地址2.端口服务扫描3.敏感目录扫描4.ffuf命令在这个目录下&#xff0c;继续使用ffuf工具扫描 5.ssh私钥爆破1.将私钥写进sh.txt中2.将私钥转换为可以被john爆破的形式3.通过John爆破 6.ssh私钥登陆7.icex64提权8.arsene提权 1.确认靶机IP地址 ┌──(roo…

浏览器F12调试

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

Tomcat之虚拟主机

1.创建存放网页的目录 mkdir -p /web/{a,b} 2.添加jsp文件 vi /web/a/index.jsp <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> <html> <head><title>JSP a page</title> </head> …

PAT-Apat甲级题1009(python和c++实现)

PTA | 1009 Product of Polynomials 1009 Product of Polynomials 作者 CHEN, Yue 单位 浙江大学 This time, you are supposed to find AB where A and B are two polynomials. Input Specification: Each input file contains one test case. Each case occupies 2 lin…

Matplotlib绘制炫酷散点图:从二维到三维,再到散点图矩阵的完整指南与实战【第58篇—python:Matplotlib绘制炫酷散点图】

文章目录 Matplotlib绘制炫酷散点图&#xff1a;二维、三维和散点图矩阵的参数说明与实战引言二维散点图三维散点图散点图矩阵二维散点图进阶&#xff1a;辅助线、注释和子图三维散点图进阶&#xff1a;动画效果和交互性散点图矩阵进阶&#xff1a;调整样式和添加密度图总结与展…

stable_diffusion提示词编写笔记(1)

stable_diffusion提示词编写笔记(1) start 总结一下AI绘画学到的知识。 一.提示词分两种&#xff1a; 1.正向提示词&#xff1b; 2.反向提示词&#xff1b; 一个对应你希望图形包含的内容提示词&#xff0c;一个对应你不希望图形出现的内容提示词。 二.如何书写提示词 1.内…

记一次VulnStack渗透

信息收集 netdiscover的主机发现部分不再详解&#xff0c;通过访问端口得知20001-2003端口都为web端口&#xff0c;所以优先考虑从此方向下手 外网渗透 GetShell Struct漏洞 访问2001端口后&#xff0c;插件Wappalyzer爬取得知这是一个基于Struct的web站点&#xff0c;直接…

gtkmm4 应用程序使用 CSS 样式

文章目录 前言css选择器css文件示例源代码效果 前言 程序样式和代码逻辑分离开 使代码逻辑更可观 css选择器 Cambalache提供了两种css-classes 相当于css里的类名:class“类名”css-name 相当于css里的标签名:spin div p 啥的 如上我设置了这个按钮控件的类名为testButton 标…

【Java】万万没想到,又被问ThreadLocal了

我待ThreadLocal如初恋&#xff0c;ThreadLocal虐我千百遍。但这一次&#xff0c;要彻底搞懂ThreadLocal 回顾一下面试名场面&#xff1a; 面试官&#xff1a;Handler如何做到与线程绑定的&#xff1f; 我&#xff1a;每个Handler只有一个相关联的Looper&#xff0c;线程绑定…

如何开始深度学习,从实践开始

将“如何开始深度学习”这个问题喂给ChatGPT和文心一言&#xff0c;会给出很有专业水准的答案&#xff0c;比如&#xff1a; 要开始深度学习&#xff0c;你可以遵循以下步骤&#xff1a; 学习Python编程语言的基础知识&#xff0c;因为它在深度学习框架中经常被使用。 熟悉线性…

自然人如何代开发票

1&#xff1a;登录国家税务总局深圳市电子税务局 地址&#xff1a;国家税务总局深圳市电子税务局 2&#xff1a;个人所得税APP 扫描登录 或 身份证登录 3&#xff1a;选择 自然人代开增值税电子普通发票 4&#xff1a;申请代开 5&#xff1a;人脸识别 6&#xff1a;画框的…

【每日一题】LeetCode——链表的中间结点

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有…

【Python4Delphi】学习笔记(一):介绍篇

一、前言&#xff1a; 1. python语言简介&#xff1a; 众所周知&#xff0c;python是目前非常流行的编程语言之一&#xff0c;自20世纪90年代初Python语言诞生至今&#xff0c;它已被逐渐广泛应用于系统管理任务的处理和Web编程。 由于Python语言的简洁性、易读性以及可扩展性…

苏宁易购移动端首页(rem布局)

技术选型 方案∶采取单独制作移动页面方案技术:布局采取rem适配布局( less rem &#xff0b;媒体查询)设计图:设计图采用750px设计尺寸 设置视口标签以及引入初始化样式 <meta name"viewport" content"widthdevice-width, initial-scale1.0, user-scalable…

Qt网络编程-写一个简单的网络调试助手

环境 Windows&#xff1a;Qt5.15.2&#xff08;VS2022&#xff09; Linux&#xff1a;Qt5.12.12&#xff08;gcc) 源代码 TCP服务器 头文件&#xff1a; #ifndef TCPSERVERWIDGET_H #define TCPSERVERWIDGET_H #include <QWidget> namespace Ui { class TCPServerW…

注意啦,MySQL8.0最新版是没有utf8选项,但是有utf8mb3和utf8mb4选项

今天在安装完MySQL最新版&#xff08;8.0.36&#xff09;&#xff0c;然后用navicat连接数据&#xff0c;创建数据库的时候&#xff0c;发现: MySQL最新版是没有utf8选项&#xff0c;但是有utf8mb3和utf8mb4选项 然后就只能卸载掉最新版&#xff0c;安装了8.0.28. &#xff08…