46-堆
目录
1.概念
2.表示
3.三大操作
4.代码实现最大堆(基于数组,编号从0开始)
4.1.根据孩子节点k获取当前父节点的索引
4.2.根据父节点k求左孩子节点下标
4.3.根据父节点k求右孩子节点下标
4.4.判空
4.5.toString()方法
4.6.判断数组中元素是否有序
4.7.查看堆顶元素
4.8.交换当前数组中 i 和 parent 的值
4.9.将value存储在堆中
4.10.元素上浮操作
4.11.取出当前堆的最大值,继续调整堆
4.12.元素下沉操作
4.13.heapify堆化操作:将任意给定的整型数组调整为堆
4.14.测试
5.总代码实现
6.总结
1.概念
- 逻辑上是一棵完全二叉树,物理上是保存在数组中。
- 基于二叉树的堆叫二叉堆。(堆的实现基本都是二叉树,还有其他的,少)
- 从节点值的要求来看,分为:
- 最大堆(Java中的叫法)/大根堆(C++中的叫法):堆中根节点的值 >= 左右子树节点值。
- 最小堆/小根堆(JDK优先级队列就是小根堆):堆中根节点的值 <= 左右子树节点值。
- 左右子树仍满足此性质。
注:
- 最大/小堆的节点大小关系与节点高度无关!
- 相同的数据下,可能有不同种类的堆,例:可有最大堆构建,也可有最小堆构建。
- 二分搜索树比堆的节点值大小关系要求更严格。
2.表示
用数组表示(动态数组可扩容),编号表示结构。(2种)
①
②
3.三大操作
- add:向堆末尾添加元素。PS:siftUp元素上浮。
- extractMax:取出当前堆的最大值元素。PS:siftDown元素下沉。
- heapify:堆化(将任意给定的整型数组调整为堆)。PS:siftDown元素下沉。
4.代码实现最大堆(基于数组,编号从0开始)
4.1.根据孩子节点k获取当前父节点的索引
/*** 根据孩子节点k获取当前父节点的索引* @param k* @return*/
private int parent(int k) {return (k - 1) >> 1; //位运算符比除法要快 return (k - 1) / 2;
}
4.2.根据父节点k求左孩子节点下标
/*** 根据父节点k求左孩子节点下标* @param k* @return*/
private int leftChild(int k) {return ( k << 1 ) + 1; //2k + 1
}
4.3.根据父节点k求右孩子节点下标
/*** 根据父节点k求右孩子节点下标* @param k* @return*/
private int rightChild(int k) {return (k << 1) + 2; //2k + 2
}
4.4.判空
/*** 判空* @return*/
public boolean isEmpty() {return data.size() == 0;
}
4.5.toString()方法
@Override
public String toString() {StringBuilder sb = new StringBuilder();sb.append("[");for (int i = 0; i < data.size(); i++) {sb.append(data.get(i));if(i != data.size() - 1){sb.append(",");}}sb.append("]");return sb.toString();
}
@Override
public String toString() {return data.toString();
}
4.6.判断数组中元素是否有序
/*** 判断数组中元素是否有序* @param arr* @return*/
public static boolean isSorted(int[] arr) {for (int i = 0; i < arr.length - 1; i++) { //i < arr.length - 1走不到最后一个元素,arr.length - 1是最后一个元素if(arr[i] < arr[i + 1]) {return false;}}return true;
}
4.7.查看堆顶元素
/*** 查看堆顶元素* @return*/
public int peekHeap() {if(data.size() == 0) {throw new NoSuchElementException("heap is empty!");}return data.get(0);
}
4.8.交换当前数组中 i 和 parent 的值
/*** 交换当前数组中 i 和 parent 的值* @param i* @param parent*/
private void swap(int i, int parent) {int tmp = data.get(i);int parentVal = data.get(parent);data.set(i,parentVal);data.set(parent,tmp);
}
4.9.将value存储在堆中
/*** 将value存储在堆中* @param value*/
public void add(int value) {//1.向数组末尾添加元素this.data.add(value);//2.调整当前堆的结构,使其仍然满足最大堆的性质siftUp(data.size() - 1);
}
4.10.元素上浮操作
/*** 元素上浮操作* @param i 要上浮的元素索引*/
private void siftUp(int i) {while(i > 0 && data.get(i) > data.get(parent(i))) {swap(i,parent(i));//继续向上判断交换后父节点向上的节点关系i = parent(i);}
}
4.11.取出当前堆的最大值,继续调整堆
/*** 取出当前堆的最大值,继续调整堆* @return*/
public int extractMax() {//判断当前堆是否为空if(data.size() == 0) {throw new NoSuchElementException("heap is empty!");}int max = data.get(0);//将最后一个元素顶到堆顶int lastElement = data.get(data.size() - 1);data.set(0, lastElement);//删除最后一个元素data.remove(data.size() - 1);//元素下沉siftDown(0);return max;
}
4.12.元素下沉操作
/*** 元素下沉操作* @param i 要下沉的元素索引*/
private void siftDown(int i) {//终止条件:当i还有子树时,说明还没判断结束;若左孩子都不存在,则一定不存在右孩子while(leftChild(i) < data.size()) {//此时i索引对应的元素仍然存在左子树,没到叶子节点int j = leftChild(i);//此时还存在右子树if(j + 1 < data.size() && data.get(j + 1 ) > data.get(j)) {//此时右子树的值大于左子树j = j + 1;}//j一定保存了左右子树的最大值索引if(data.get(i) >= data.get(j)) { //此处为何是 >=//如果此处终止条件是 父节点 > 子节点,也可以,不会死循环//以后在写测试用例时,必须要复杂//此时i对应的元素已经下沉到合适位置break;}else{swap(i,j);i = j;}}
}
4.13.heapify堆化操作:将任意给定的整型数组调整为堆
/*** heapify堆化操作:将任意给定的整型数组调整为堆* ①自底向上逐渐调整堆的过程,只看非叶子节点,从最后一个非叶子节点(最后一个非叶子节点就是最后一个节点他爸)开始,一次性砍掉所有叶子节点,进行元素的siftDown操作,直到向上走到根节点即可,时间复杂度为O(n)* ②遍历原数组,依次add操作,时间复杂度为O(nlogn)* @param arr*/
public MaxHeap(int[] arr) {//初始化data = new ArrayList<>(arr.length);//1.先将arr的所有元素拷贝到data中for(int i : arr) {data.add(i);}//2.从最后一个非叶子节点开始进行元素下沉for (int i = parent(data.size() - 1); i >= 0 ; i--) {siftDown(i);}
}
4.14.测试
public static void main(String[] args) {
// MaxHeap heap = new MaxHeap();
// heap.add(5);
// heap.add(2);
// heap.add(7);
// heap.add(1);
// heap.add(3);
// System.out.println(heap);
//
// int[] ret = new int[5];
// for (int i = 0; i < ret.length; i++) {
// ret[i] = heap.extractMax();
// }
// System.out.println(Arrays.toString(ret));// int[] data = {17,90,68,12,15,14,70,30,20};
// MaxHeap heap = new MaxHeap(data.length);
// //依次调用extractMax()->集合恰好是一个降序集合
// for (int i = 0; i < data.length; i++) {
// heap.add(data[i]);
// }
// for (int i = 0; i < data.length; i++) {
// data[i] = heap.extractMax();
// }
// System.out.println(Arrays.toString(data));// int[] data = {17,90,68,12,15,14,70,30,20};
// MaxHeap heap = new MaxHeap(data);
// //依次调用extractMax()->集合恰好是一个降序集合
// for (int i = 0; i < data.length; i++) {
// data[i] = heap.extractMax();
// }
// System.out.println(Arrays.toString(data));int n = 10000;int[] data = new int[n];//生成随机数Random random = new Random();for (int i = 0; i < n; i++) {//范围是 0 - Integer.MAX_VALUEdata[i] = random.nextInt(Integer.MAX_VALUE);}MaxHeap heap = new MaxHeap(data);for (int i = 0; i < n; i++) {data[i] = heap.extractMax();}System.out.println(isSorted(data));
}
5.总代码实现
import java.util.*;/*** 基于数组实现的最大堆* 编号从0开始,假设当前节点为i,i>0* parent = (i - 1) / 2;* 若有左右孩子,保证 2i + 1 或 2i + 2 < data.length;* left = 2i + 1;* right = 2i + 2;*/
public class MaxHeap {//具体存储元素的动态数组private List<Integer> data;//无参构造public MaxHeap() {this(10);}//指定容量大小public MaxHeap(int initCap) {this.data = new ArrayList<>(initCap);}/*** 根据孩子节点k获取当前父节点的索引* @param k* @return*/private int parent(int k) {return (k - 1) >> 1; //位运算符比除法要快 return (k - 1) / 2;}/*** 根据父节点k求左孩子节点下标* @param k* @return*/private int leftChild(int k) {return ( k << 1 ) + 1; //2k + 1}/*** 根据父节点k求右孩子节点下标* @param k* @return*/private int rightChild(int k) {return (k << 1) + 2; //2k + 2}/*** 判空* @return*/public boolean isEmpty() {return data.size() == 0;}@Overridepublic String toString() {return data.toString();}/*** 判断数组中元素是否有序* @param arr* @return*/public static boolean isSorted(int[] arr) {for (int i = 0; i < arr.length - 1; i++) { //i < arr.length - 1走不到最后一个元素,arr.length - 1是最后一个元素if(arr[i] < arr[i + 1]) {return false;}}return true;}/*** 查看堆顶元素* @return*/public int peekHeap() {if(data.size() == 0) {throw new NoSuchElementException("heap is empty!");}return data.get(0);}/*** 交换当前数组中 i 和 parent 的值* @param i* @param parent*/private void swap(int i, int parent) {int tmp = data.get(i);int parentVal = data.get(parent);data.set(i,parentVal);data.set(parent,tmp);}/*** 将value存储在堆中* @param value*/public void add(int value) {//1.向数组末尾添加元素this.data.add(value);//2.调整当前堆的结构,使其仍然满足最大堆的性质siftUp(data.size() - 1);}/*** 元素上浮操作* @param i 要上浮的元素索引*/private void siftUp(int i) {while(i > 0 && data.get(i) > data.get(parent(i))) {swap(i,parent(i));//继续向上判断交换后父节点向上的节点关系i = parent(i);}}/*** 取出当前堆的最大值,继续调整堆* @return*/public int extractMax() {//判断当前堆是否为空if(data.size() == 0) {throw new NoSuchElementException("heap is empty!");}int max = data.get(0);//将最后一个元素顶到堆顶int lastElement = data.get(data.size() - 1);data.set(0, lastElement);//删除最后一个元素data.remove(data.size() - 1);//元素下沉siftDown(0);return max;}/*** 元素下沉操作* @param i 要下沉的元素索引*/private void siftDown(int i) {//终止条件:当i还有子树时,说明还没判断结束;若左孩子都不存在,则一定不存在右孩子while(leftChild(i) < data.size()) {//此时i索引对应的元素仍然存在左子树,没到叶子节点int j = leftChild(i);//此时还存在右子树if(j + 1 < data.size() && data.get(j + 1 ) > data.get(j)) {//此时右子树的值大于左子树j = j + 1;}//j一定保存了左右子树的最大值索引if(data.get(i) >= data.get(j)) { //此处为何是 >=//如果此处终止条件是 父节点 > 子节点,也可以,不会死循环//以后在写测试用例时,必须要复杂//此时i对应的元素已经下沉到合适位置break;}else{swap(i,j);i = j;}}}/*** heapify堆化操作:将任意给定的整型数组调整为堆* ①自底向上逐渐调整堆的过程,只看非叶子节点,从最后一个非叶子节点(最后一个非叶子节点就是最后一个节点他爸)开始,一次性砍掉所有叶子节点,进行元素的siftDown操作,直到向上走到根节点即可,时间复杂度为O(n)* ②遍历原数组,依次add操作,时间复杂度为O(nlogn)* @param arr*/public MaxHeap(int[] arr) {//初始化data = new ArrayList<>(arr.length);//1.先将arr的所有元素拷贝到data中for(int i : arr) {data.add(i);}//2.从最后一个非叶子节点开始进行元素下沉for (int i = parent(data.size() - 1); i >= 0 ; i--) {siftDown(i);}}public static void main(String[] args) {
// MaxHeap heap = new MaxHeap();
// heap.add(5);
// heap.add(2);
// heap.add(7);
// heap.add(1);
// heap.add(3);
// System.out.println(heap);
//
// int[] ret = new int[5];
// for (int i = 0; i < ret.length; i++) {
// ret[i] = heap.extractMax();
// }
// System.out.println(Arrays.toString(ret));// int[] data = {17,90,68,12,15,14,70,30,20};
// MaxHeap heap = new MaxHeap(data.length);
// //依次调用extractMax()->集合恰好是一个降序集合
// for (int i = 0; i < data.length; i++) {
// heap.add(data[i]);
// }
// for (int i = 0; i < data.length; i++) {
// data[i] = heap.extractMax();
// }
// System.out.println(Arrays.toString(data));// int[] data = {17,90,68,12,15,14,70,30,20};
// MaxHeap heap = new MaxHeap(data);
// //依次调用extractMax()->集合恰好是一个降序集合
// for (int i = 0; i < data.length; i++) {
// data[i] = heap.extractMax();
// }
// System.out.println(Arrays.toString(data));int n = 10000;int[] data = new int[n];//生成随机数Random random = new Random();for (int i = 0; i < n; i++) {//范围是 0 - Integer.MAX_VALUEdata[i] = random.nextInt(Integer.MAX_VALUE);}MaxHeap heap = new MaxHeap(data);for (int i = 0; i < n; i++) {data[i] = heap.extractMax();}System.out.println(isSorted(data));}
}
6.总结
将任意数组调整为堆,而后依次extractMax的操作得到的就是一个排序数组——时间复杂度O(nlogn)。
需要开辟一个和原数组大小完全相同的临时空间——空间复杂度O(n)。
优化:原地堆排序。
相关文章:

46-堆
目录 1.概念 2.表示 3.三大操作 4.代码实现最大堆(基于数组,编号从0开始) 4.1.根据孩子节点k获取当前父节点的索引 4.2.根据父节点k求左孩子节点下标 4.3.根据父节点k求右孩子节点下标 4.4.判空 4.5.toString()方法 4.6.判断数组中…...
Mysql高可用高性能存储应用系列3 - mysqld_multi配置主从集群
概述 主从复制要解决的问题,1)写操作锁表,影响读操作,影响业务。2)数据库备份。3)随着数据增加,I/O操作增多,单机出现瓶颈。 主从复制就是从服务器的主节点,复制到多个从节点,默认采用异步的方…...
天干地支(Java)
题目描述 古代中国使用天干地支来记录当前的年份。 天干一共有十个,分别为:甲(jiǎ)、乙(yǐ)、丙(bǐng)、丁(dīng)、戊(w)、己&a…...

码住,虹科工业树莓派应用小tips
在应用虹科工业树莓派进行项目开发的过程中,我们会应用到各种功能,部分功能看似不起眼,但是在实际应用开发过程中却非常重要。接下来虹科分享几个工业树莓派在应用过程中经常会遇到的几个问题,并分享解决方案,帮助大家…...

美国新规-带绳窗帘亚马逊ANSI/WCMA A100.1-20测试标准详解
亚马逊要求所有有线窗帘都经过测试,符合下列特定法规或标准要求: 商品法规/标准要求带绳窗帘以下所有项: 显示检测结果符合 ANSI/WCMA A100.1-2018(带绳窗帘商品的美国国家安全标准)的检测报告。 美国消费品安全委员…...
【华为OD机试 2023最新 】 模拟商场优惠打折(C++)
题目描述 模拟商场优惠打折,有三种优惠券可以用,满减券、打折券和无门槛券。 满减券:满100减10,满200减20,满300减30,满400减40,以此类推不限制使用; 打折券:固定折扣92折,且打折之后向下取整,每次购物只能用1次; 无门槛券:一张券减5元,没有使用限制。 每个…...

前端直接生成GIF动态图实践
前言去年在博客中发了两篇关于GIF动态生成的博客,GIF图像动态生成-JAVA后台生成和基于FFmpeg的Java视频Mp4转GIF初探,在这两篇博客中都是采用JAVA语言在后台进行转换。使用JAVA的同学经过自己的改造和开发也可以应用在项目上。前段时间有朋友私下问&…...

2023年Java岗面试八股文及答案整理(金三银四最新版)
春招,秋招,社招,我们Java程序员的面试之路,是挺难的,过了HR,还得被技术面,小刀在去各个厂面试的时候,经常是通宵睡不着觉,头发都脱了一大把,还好最终侥幸能够…...
centos8上安装redis
一、安装前准备 在安装Redis之前,需要确保CentOS 8系统已经安装了EPEL存储库和Redis的依赖库。 安装EPEL存储库 EPEL存储库是一个由Fedora项目提供的额外软件包仓库,包含了许多常用的软件包。在CentOS 8系统上,可以通过以下命令安装EPEL存储…...

新六级阅读通关特训
词汇题(55道) 1. You should carefully think over_____ the manager said at the meeting. A. that B. which C. what D. whose 1.选C,考察宾语从句连接词,主句谓语动词think over后面缺宾语,后面的宾语从句谓语动…...

【AI绘画】如何使用Google Colab安装Stable Diffusion
【AI绘画】如何在Colab安装的Stable Diffusion背景准备安装查看资源仓库跳转到Colab运行Stable Diffusion基础设置启动运行访问Stable Diffusion WebUI界面模型资源推荐背景 本地部署Stable Diffusion过程麻烦,对机器配置要求高,GPU 4G,然而…...

C++:STL架构图
STL架构图1:仿函数2:算法架构图算法库 再看一下这个实例 #include<vector> #include<algorithm> #include<functional> #include<iostream> using namespace std;int main() {int i[6] {1,2,3,4,5,6};vector<int,allocato…...

[Ubuntu][网络][教程]端口转发以及端口管理
1. 平台介绍 Ubuntu 20.04 LTS Armv7 2. 端口管理 进行端口转发之前,要先对端口进行一系列设置 2.1 安装ufw sudo apt install ufw2.2 开启22端口 开启ufw之后,默认的22端口不会自动打开,使用SSH的话需要手动打开 sudo ufw allow 22…...
@Scheduled 定时任务不执行
一、排查代码中添加的定时任务步骤是否正确 启动类上加 EnableScheduling 注解定时任务类上加Component定时方法上加Scheduled Scheduled(cron "0 19 16 * * ?")public void cron() {log.info("定时任务开启:---");}二、排查是否任务阻塞&am…...
我是怎样被卷的(二)
被卷的过程,虽然是辛苦种种(加班熬夜陪着爆肝),但终有所值。没有这样的高压环境,我都不知道自己居然可以这么的优秀。 我要答复的问题,分为4类。一是我自己已经掌握的,二是需要找别人获取的&am…...

Linux- 浅谈ELF目标文件格式
理解了进程的描述和创建之后,自然会想到我们编写的可执行程序是如何作为一个进程工作的?这就涉及可执行文件的格式、编译、链接和装载等相关知识。 这里先提一个常见的名词“目标文件”,是指编译器生成的文件。“目标”指目标平台,…...
C++ MVC模式
概述 C是一种流行的编程语言,它可以用于构建各种类型的应用程序,包括Web应用程序、桌面应用程序和移动应用程序。在这里,我将为您介绍C中的MVC模式,以及如何在C中实现MVC模式。 MVC(Model-View-Controller࿰…...

IntelliJ IDEA2021安装教程
1.鼠标右击【JetBrains 2021】压缩包(win11系统需先点击“显示更多选项”)选择【解压到“JetBrains 2021”】 2.打开解压后的文件夹,鼠标右击您需要安装的软件名称(如:IdealU-2021.3.1)选择【以管理员身份运…...

day16—选择题
文章目录1.计算每位学生的多学科加总成绩的SQL是(C)2.以下哪个不是与Mysql服务器相互作用的通讯协议(B)3.设有两个事务T1,T2,其并发操作如下所示,下面评价正确的是(D)4.如果事务T获得了数据项Q上的排它锁&a…...
LLVM 的中间代码(IR) 基本语法
LLVM 的中间代码(IR) 基本语法 以下是 LLVM IR 的基本语法和每个语法的实例代码: 1.数据类型 LLVM IR 支持多种数据类型,包括整型、浮点型、指针型和向量型等。以下是 LLVM IR 中定义不同类型的语法和示例代码: 整…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...