Java 数据结构篇-实现堆的核心方法与堆的应用(实现 TOP-K 问题:最小 k 个数)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
  

 

文章目录

        1.0 堆的说明

        2.0 堆的成员变量及其构造方法 

        3.0 实现堆的核心方法

        3.1 实现堆的核心方法 - 获取堆顶元素 peek()

        3.2 实现堆的核心方法 - 下潜 down(int i)

        3.3 实现堆的核心方法 - 交换元素 swap(int i,int j)

        3.4 实现堆核心方法 - 删除堆顶元素 poll()

        3.5 实现堆的核心方法 - 替换堆顶元素 replace(int i)

        3.6 实现堆的核心方法 - 添加元素 offer(int value)

        3.7 实现堆的核心方法 - 建堆 heapify()

        3.8 实现堆的核心方法完整代码

        4.0 TOP - K 问题:最小的 K 个数

        4.1 实现最小 k 个数的思路

        4.2 代码实现最小 k 个数


        1.0 堆的说明

        堆(Heap)是一种基于树的数据结构,通常用于动态分配内存空间。堆可以被看作是一棵完全二叉树,其中每个节点都满足堆的性质,即父节点的值大于或等于子节点的值(大根堆),或父节点的值小于或等于子节点的值(小根堆)。在堆中,根节点的值是最大或最小的,因此也被称为最大堆或最小堆。

        堆的实现通常使用数组来存储堆中的元素通过计算数组下标来实现节点之间的关系。堆的时间复杂度为 O(log n),其中 n 是堆中元素的数量。

        堆的操作包括插入删除查找等。插入操作将一个新元素插入到堆中,删除操作将堆中的最大或最小元素删除,查找操作可以在堆中查找特定元素的位置。

        2.0 堆的成员变量及其构造方法 

        主要的成员变量为:int[] arr 数组:用来存放元素的容器;int size :代表当前的元素个数。

        构造方法:指定数组大小带参数的构造器参数为数组的构造器

代码如下:

public class MyHeap {public int[] arr;public int size;public MyHeap(int capacity) {arr = new int[capacity];}public MyHeap(int[] arr) {this.arr = arr;this.size = arr.length;heapify();}}

        

        3.0 实现堆的核心方法

        获取堆顶元素、下潜、交换元素、添加元素、替换元素、删除元素、建堆。

        3.1 实现堆的核心方法 - 获取堆顶元素 peek()

        用数组实现堆,在获取堆顶元之前,先需要判断该数组是否为空,若不为空,则直接返回数组索引为 0 的元素;若数组为空,则返回 0 或者抛出异常也可以。

代码如下:

    //获取栈顶元素public int peek() {if (isEmpty()) {return -1;}return arr[0];}

        3.2 实现堆的核心方法 - 下潜 down(int i)

        该方法主要用于删除栈顶元素、替换元素等核心方法。下潜的意思就是将当前的元素所在的位置不符合大顶堆或者小顶堆的规则,因此需要向下调整。找到合适的位置来存放当前的元素

 具体下潜的思路:

假设需要满足大顶堆的规则:

        由以上的图来看,当前的索引为 0 处的元素 7 小于该左孩子的元素,因此当前不满足大顶堆的规则。需要将两者进行交换。

交换的结果为:

        交换完之后,当前索引为 2 处的元素 7 小于该右孩子的元素,所以索引 2 与 索引 5 需要继续交换。若当前为 i 该右孩子的索引 left = 2 * i + 1;该左孩子的索引 right = 2 * i + 2 (也可以表示为 right = left + 1 )一开始默认当前的最大元素的索引 max = i ,接着来判断该左右孩子的元素是否大于当前索引 max ,若大于当前索引 max 时,需要进行交换 max = left 或者 max = right 。若不大于当前索引为 max 处的元素,则不需要交换。由于每一次都是子问题过程,所以可以利用递归来实现,当且仅当 max != i 时,说明 max 已经被交换过了,需要继续向下递出,直到 max == i 时,结束递出,开始回溯。当然,这里不需要回带任何值或者变量,即该递归函数的返回类型为 viod 。

代码如下:

    //下潜public void down(int i) {int left = 2 * i + 1;int right = left + 1;int max = i;if (left < size && arr[left] > arr[max]) {max = left;}if (right < size && arr[right] > arr[max]) {max = right;}if (max != i) {//交换swap(i,max);//继续下潜down(max);}}

        3.3 实现堆的核心方法 - 交换元素 swap(int i,int j)

        交换数组索引中 i 与 j 处的元素

代码如下:

    //交换public void swap(int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}

        3.4 实现堆核心方法 - 删除堆顶元素 poll()

        具体实现思路:为了更高效率的删除堆顶元素后保持原来大顶堆或者小顶堆的规则。

        步骤一:先将堆顶元素与最后一个元素进行交换。即 arr[0] = arr[size - 1] 。

        步骤二:将 size-- 。

        步骤三:交换后的堆,可能会不满足大顶堆或者小顶堆的规则,则需要将堆顶元素进行下潜调整,找到合适的位置存放该元素。最后需要返回删除的元素。

代码如下:

    //删除堆顶元素public int poll() {if (isEmpty()) {return 0;}int t = arr[0];arr[0] = arr[size - 1];size--;//下潜down(0);return t;}

        注意:在删除堆顶元素之前,需要先判断当前的数组是否为空数组。

        同理,若需要删除指定堆中的元素索引,实现思路是一样的。

代码如下:

    //指定删除元素public int poll(int i) {if (i > size) {return 0;}int temp = arr[i];arr[i] = arr[size - 1];size--;//下潜down(i);return temp;}

        先判断索引是否合法,若不合法,则返回 0 或者抛出异常也可以。

        3.5 实现堆的核心方法 - 替换堆顶元素 replace(int i)

        具体思路为:先判断该数组是否为空数组,若不为空数组,则直接替换堆顶元素 arr[0] = i;之后可能会不满足大顶堆或者小顶堆的规则,所以索引为 0 处需要下潜调整,找到合适的位置存放元素。

代码实现:

    //替换堆顶元素public void replace(int i) {if (isEmpty()) {return;}arr[0] = i;down(0);}

        3.6 实现堆的核心方法 - 添加元素 offer(int value)

        具体实现思路:先判断当前索引为 i = size 处的双亲节点为 j = (i - 1) / 2 ,判断 arr[j] 与 value 的大小,若为大顶堆规则,则当 arr[j] > value 时,不需要继续往上走了,在 i 处存放 value 即可 arr[i] = value ;当 arr[j] <= value 时,先将 arr[j] 处的元素存放在 arr[i] 中,接着需要继续往上走, i = j ,j = (i - 1) / 2 直到 i == 0 或者 arr[j] > value 时,退出循环。在 arr[i] 处存放 value

代码如下:

    //添加元素public boolean offer(int value) {if (isFull()) {return false;}int i = size;int j = (size - 1) / 2;while (i > 0 && arr[j] < value) {arr[i] = arr[j];i = j;j = (i - 1) / 2;}arr[i] = value;size++;return true;}

        需要注意:添加元素前,需要先判断该数组是否满了。还有添加完之后,需要进行 size++

        3.7 实现堆的核心方法 - 建堆 heapify()

        该方法实现的意义,若随机给出一个数组,需要实现由大顶堆或者小顶堆的结构存放元素。因此就会用到该方法。

        实现思路为:需要找到最后一个非叶子节点,从后往前,将当前的元素进行下潜处理即可完成建堆。

代码如下:

    //建堆public void heapify() {//先找到最后非叶子节点int lastNonLeafNodes = size / 2 - 1;for (int i = lastNonLeafNodes; i >= 0 ; i--) {//下潜down(i);}}

        3.8 实现堆的核心方法完整代码

public class MyHeap {public int[] arr;public int size;public MyHeap(int capacity) {arr = new int[capacity];}public MyHeap(int[] arr) {this.arr = arr;this.size = arr.length;heapify();}//获取栈顶元素public int peek() {if (isEmpty()) {return -1;}return arr[0];}//删除堆顶元素public int poll() {if (isEmpty()) {return 0;}int t = arr[0];arr[0] = arr[size - 1];size--;//下潜down(0);return t;}//指定删除元素public int poll(int i) {if (i > size) {return 0;}int temp = arr[i];arr[i] = arr[size - 1];size--;//下潜down(i);return temp;}//替换堆顶元素public void replace(int i) {if (isEmpty()) {return;}arr[0] = i;down(0);}//添加元素public boolean offer(int value) {if (isFull()) {return false;}int i = size;int j = (size - 1) / 2;while (i > 0 && arr[j] < value) {arr[i] = arr[j];i = j;j = (i - 1) / 2;}arr[i] = value;size++;return true;}//建堆public void heapify() {//先找到最后非叶子节点int lastNonLeafNodes = size / 2 - 1;for (int i = lastNonLeafNodes; i >= 0 ; i--) {//下潜down(i);}}//下潜public void down(int i) {int left = 2 * i + 1;int right = left + 1;int max = i;if (left < size && arr[left] > arr[max]) {max = left;}if (right < size && arr[right] > arr[max]) {max = right;}if (max != i) {//交换swap(i,max);//继续下潜down(max);}}//交换public void swap(int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}//判断是否为空数组public boolean isEmpty() {return size == 0;}//判断是否为满数组public boolean isFull() {return  size == arr.length;}
}

        4.0 TOP - K 问题:最小的 K 个数

题目:

        设计一个算法,找出数组中最小的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))

OJ 链接:

面试题 17.14. 最小K个数

        4.1 实现最小 k 个数的思路

        具体思路为:结合大顶堆的数据结构的特点,根节点的元素永远比孩子节点的元素大。先将给定的 arr 数组的前 k 个元素直接通过 heap.offer() 方法添加到大顶堆上,然后 arr 数组剩下的元素需要跟堆顶元素相对比,若堆顶元素大于 arr[i] 中的元素,则需要进行交换,将 arr[i] 的元素替换到堆顶,接着还不能结束,有可能替换完的元素就不符合大顶堆的规则了,因此还需要将堆顶元素下潜处理调整,找到合适的位置存放该元素;若堆顶元素不大于 arr[i] 中的元素,则不需要交换。一直将 arr 数组中的元素遍历结束,则循环停止。最后堆上存储的 k 个元素就是该数组 arr 中最小的元素了。

        4.2 代码实现最小 k 个数

public class Solution {public int[] smallestK(int[] arr, int k) {MaxHeap heap = new MaxHeap(k);for(int i = 0; i < k ; i++) {heap.offer(arr[i]);}for(int i = k; i < arr.length; i++) {if(heap.peek() > arr[i]) {heap.arr[0] = arr[i];heap.down(0);}}return heap.arr;}}//实现一个大顶堆
class MaxHeap {int[] arr;int size;public MaxHeap(int capacity) {arr = new int[capacity];}public MaxHeap(int[] smallestK) {this.arr = smallestK;this.size = smallestK.length;}//插入元素public boolean offer(int value) {if(isFull()) {return false;}int i = size;int j = (i - 1) / 2;while(i > 0 && arr[j] < value) {arr[i] = arr[j];i = j;j = (i - 1) / 2;}arr[i] = value;size++;return true;}//删除堆顶元素public int poll() {if(isEmpty()) {return 0;}int ret = arr[0];arr[0] = arr[size - 1];size--;down(0);return ret;}//下潜public void down(int i) {int left = 2 * i + 1;int right = left + 1;int max = i;if(left < size && arr[left] > arr[max]) {max = left;}if(right < size && arr[right] > arr[max]) {max = right;}if(max != i) {swap(max,i);down(max);}}//交换public void swap(int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}//获取堆顶元素public int peek() {if(isEmpty()) {return 0;}return arr[0];}//判断是否为空public boolean isEmpty() {return size == 0;}//判断是否为满public boolean isFull() {return size == arr.length;}}

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

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

相关文章

嵌入式串口输入详细实例

学习目标 掌握串口初始化流程掌握串口输出单个字符掌握串口输出字符串掌握通过串口printf熟练掌握串口开发流程学习内容 需求 串口循环输出内容到PC机。 串口数据发送 添加Usart功能。 首先,选中Firmware,鼠标右键,点击Manage Project Items 接着,将gd32f4xx_usart.c添…

xcode无线真机调试详细图文步骤

步骤一、 步骤二&#xff1a; 步骤三&#xff1a; 配置完到这里&#xff0c;点击真机右键&#xff0c;菜单栏并未出现connect via ip address 选项&#xff0c;也没出现无线连接的小地球图标&#xff0c;别慌&#xff0c;接着进行下一步操作即可。 步骤四&#xff1a; 1.打开…

什么是同源策略?

同源 同源指的是URL有相同的协议、主机名和端口号。 同源策略 同源策略指的是浏览器提供的安全功能&#xff0c;非同源的RUL之间不能进行资源交互 跨域 两个非同源之间要进行资源交互就是跨域。 浏览器对跨域请求的拦截 浏览器是允许跨域请求的&#xff0c;但是请求返回…

手机端升级ChatGPT失败怎么办?解决方案

昨天一个朋友说他手机端升级失败如下图 其实手机端对IP环境要求更严格&#xff0c;升级失败很正常&#xff0c;解决办法就是使用电脑端就可以了&#xff0c;电脑端对IP环境相对来说没有手机那么严格 不管手机电脑&#xff0c;都要用最纯净的IP才行&#xff0c;如果不行&#xf…

MyBatis——MyBatis的CRUD(增删改查)

1.MyBatis的CRUD 创建工程&#xff1a; 1.1.查询 1.1.1.单个参数绑定 //单个参数传递public User findUserById(Integer id);<!--parameterType:指定输入参数的类型resultType&#xff1a;指定数据结果封装的数据类型#{id}&#xff1a;它代表占位符&#xff0c;相当于原来…

大数据讲课笔记5.1 初探MapReduce

文章目录 零、学习目标一、导入新课二、新课讲解&#xff08;一&#xff09;MapReduce核心思想&#xff08;二&#xff09;MapReduce编程模型&#xff08;三&#xff09;MapReduce编程实例——词频统计思路1、Map阶段&#xff08;映射阶段&#xff09;2、Reduce阶段&#xff08…

【Java异常】idea 报错:无效的目标发行版:17 的解决办法

【Java异常】idea 报错&#xff1a;无效的目标发行版&#xff1a;17 的解决办法 一&#xff0c;问题来源 springcloud的第一个demo项目就给我干趴了 二、原因分析 java: 无效的目标发行版: 17 原因就是 JDK 版本不对。从 IDEA 编辑器中可以找到问题的原因所在&#xff0c;…

ImportError: cannot import name ‘calcsize‘ from ‘struct‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

英文表示数字_分支结构 C语言xdoj146

题目描述&#xff1a;输入一个0~9的一位整数&#xff0c;输出其对应的英文单词。要求用switch结构完成。 示例&#xff1a; 输入&#xff1a;0 输出&#xff1a;zero 输入&#xff1a;8 输出&#xff1a;eight #include <stdio.h>//英文表示数字_分支结构 int main() {in…

爬虫中scrapy模块的概念作用和工作流程

scrapy的概念和流程 学习目标&#xff1a; 了解 scrapy的概念了解 scrapy框架的作用掌握 scrapy框架的运行流程掌握 scrapy中每个模块的作用 1. scrapy的概念 Scrapy是一个Python编写的开源网络爬虫框架。它是一个被设计用于爬取网络数据、提取结构性数据的框架。 Scrapy 使…

状态管理之复杂对象

前面我们学习的State、Prop、Link、Provide、Consume这些装饰器都只能更新对象的直接赋值、对象属性赋值&#xff1b;如果对象的属性又是一个对象&#xff0c;也就是嵌套对象&#xff0c;那么对嵌套对象的属性的更新是不会被观察到的&#xff0c;所以复杂状态管理用于解决该问题…

前后端分离跨域问题的OPTIONS请求

本篇文章用于个人的问题记录 问题描述: 使用了springbootvue3做前后端分离,使用sa-token做登录认证 由于sa-token的前后端分离的登录认证需要在请求发起时自定义添加头部satoken 好那么问题来了,我请求的时候看我的请求头是存在satoken这个头部信息的 但我在springboot的拦截…

【sprintboot+vue3】解决前后端分离项目遇到的问题

目录 一、Access to XMLHttpRequest at http://127.0.0.1:8088/api/hello from origin http://localhost:5173 has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource. 二、报错[vue/compiler-sfc] 一、Access to …

uniapp笔记

/pages/component/swiper/swiper /pages/component/button/button navigator image 设置界面标题 页面跳转 设置TabBar 发起一个请求 网络请求

明理信息科技打造专属个人或企业知识付费平台,核心功能设计

在当今信息爆炸的时代&#xff0c;知识管理已经成为了每个人必须面对的问题。然而&#xff0c;市面上的知识付费平台大多数都是通用的&#xff0c;无法满足个性化需求。 因此&#xff0c;明理信息科技提供了一款专属定制的适合个人的知识付费平台。核心产品能力如下&#xff1…

画图之C4架构图idea和vscode环境搭建篇

VS Code 下C4-PlantUML安装 安装VS Code 直接官网下载安装即可,过程略去。 安装PlantUML插件 在VS Code的Extensions窗口中搜索PlantUML,安装PlantUML插件。 配置VS Code代码片段 安装完PlantUML之后,为了提高效率,我们最好安装PlantUML相关的代码片段。 打开VS Cod…

Java精品项目源码新基于协同过滤算法的旅游推荐系统(编号V69)

Java精品项目源码新基于协同过滤算法的旅游推荐系统(编号V69) 大家好&#xff0c;小辰今天给大家介绍一个基于协同过滤算法的旅游推荐系统

WPF Halcon机器视觉和运动控制软件通用框架,插件式开发,开箱即用 仅供学习!

点我下载&#xff0c;仅供个人学习使用 参考easyvision开发&#xff0c;集成几十个软件算子此版本以添加ui设计器。具体功能如上所示&#xff0c;可以自定义变量&#xff0c;写c#脚本&#xff0c;自定义流程&#xff0c;包含了halcon脚本和封装的算子&#xff0c;可自定义ui&a…

MySQL的增删改查(进阶)--上

1. 数据库约束 1.1 约束类型 NOT NULL - 指示某列不能存储 NULL 值。 UNIQUE - 保证某列的每行必须有唯一的值。 DEFAULT - 规定没有给列赋值时的默认值。 PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列&#xff08;或两个列多个列的结合&#xff09;有唯一标识&#xf…

需要token的原因----

需要token的原因主要有以下几点&#xff1a; 安全性&#xff1a;Token的使用可以增强用户信息的安全性。在用户向服务端请求数据时&#xff0c;服务端需要通过数据库来判断用户名和密码是否正确&#xff0c;以确定是否提供所需内容。Token作为一层额外的验证&#xff0c;可以验…