Java 与排序算法(7):堆排序

news2024/9/21 2:41:14

一、堆排序

堆排序是一种基于比较的排序算法,它的基本思想是将待排序的元素构建成一个堆,然后依次将堆顶元素取出,放到已排序的序列中,直到堆中所有元素都被取出,最终得到一个有序的序列。

堆是一种特殊的树形数据结构,它满足以下两个条件:

  1. 堆是一颗完全二叉树。

  2. 堆中每个节点的值都大于等于(或小于等于)其左右子节点的值,这种性质称为堆的性质。

堆排序的基本步骤如下:

  1. 构建堆:将待排序的元素构建成一个堆,可以使用数组来表示堆,数组中下标为 i 的元素的左子节点下标为 2i+1,右子节点下标为 2i+2。

  2. 取出堆顶元素:将堆顶元素取出,放到已排序的序列中。

  3. 调整堆:将剩余的元素重新构建成一个堆,然后重复步骤 2 和步骤 3,直到堆中所有元素都被取出。

在这里插入图片描述

堆排序的优点是排序效率高,适用于大规模数据的排序。堆排序的缺点是不稳定,无法保证相等元素的相对位置不变。

二、堆排序的性质

堆排序的性质如下:

  1. 堆是一种完全二叉树:堆是一种特殊的树形数据结构,它满足完全二叉树的性质,即除了最后一层外,其他层的节点数都是满的,最后一层的节点都靠左排列。

  2. 堆分为大根堆和小根堆:大根堆中每个节点的值都大于等于其左右子节点的值,小根堆中每个节点的值都小于等于其左右子节点的值。

  3. 堆的性质:堆中每个节点的值都大于等于(或小于等于)其左右子节点的值,这种性质称为堆的性质。

  4. 堆的存储方式:堆可以使用数组来表示,数组中下标为 i 的元素的左子节点下标为 2i+1,右子节点下标为 2i+2。

  5. 堆排序的过程:堆排序的过程包括构建堆、取出堆顶元素、调整堆三个步骤。在构建堆的过程中,需要从下往上调整堆,确保堆中每个节点都满足堆的性质;在取出堆顶元素的过程中,需要将堆顶元素与堆中最后一个元素交换位置,然后将堆中剩余元素重新构建成一个堆,从而保证堆的性质;在调整堆的过程中,需要从上往下调整堆,确保堆中每个节点都满足堆的性质。

  6. 堆排序的时间复杂度:堆排序的时间复杂度为 O(nlogn),其中 n 表示待排序数组的长度。堆排序的时间复杂度比较稳定,不受数据分布的影响。

  7. 堆排序的空间复杂度:堆排序的空间复杂度为 O(1),因为堆排序是原地排序算法,不需要额外的空间来存储临时数组。

三、堆排序的变种

堆排序的变种有以下几种:

  1. 堆排序的升级版:堆排序的升级版是优先队列(Priority Queue),它是一种支持在 O(logn) 时间内插入和删除元素的数据结构,可以使用堆来实现。在 Java 中,PriorityQueue 就是使用堆来实现的。

  2. 大小根堆的切换:堆排序可以通过切换大小根堆来实现升序和降序排序。如果使用大根堆,则可以实现升序排序,因为大根堆的堆顶元素是最大的;如果使用小根堆,则可以实现降序排序,因为小根堆的堆顶元素是最小的。

  3. 堆排序的非递归实现:堆排序可以使用非递归的方式来实现,这样可以避免递归调用带来的额外开销。非递归的堆排序可以使用循环来实现,具体实现方法可以参考迭代版堆排序。

  4. 堆排序的并行实现:堆排序可以使用多线程来实现并行排序,这样可以加速排序的过程。具体实现方法可以参考并行堆排序。

  5. 堆排序的优化:堆排序的效率可以通过一些优化来提高,例如使用局部性原理来减少缓存的失效、使用指针来代替数组下标来提高访问效率等。

四、Java 实现

以下是 Java 实现堆排序的代码:

public class HeapSort {
    public static void heapSort(int[] arr) {
        if (arr == null || arr.length == 0) {
            return;
        }
        int len = arr.length;
        // 构建大根堆
        for (int i = len / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, len);
        }
        // 取出堆顶元素,放到已排序的序列中
        for (int i = len - 1; i >= 0; i--) {
            swap(arr, 0, i);
            adjustHeap(arr, 0, i);
        }
    }

    private static void adjustHeap(int[] arr, int i, int len) {
        int temp = arr[i];
        for (int j = i * 2 + 1; j < len; j = j * 2 + 1) {
            if (j + 1 < len && arr[j] < arr[j + 1]) {
                j++;
            }
            if (arr[j] > temp) {
                arr[i] = arr[j];
                i = j;
            } else {
                break;
            }
        }
        arr[i] = temp;
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

在上面的代码中,我们首先构建一个大根堆,然后依次取出堆顶元素,放到已排序的序列中,最终得到一个有序的序列。在构建大根堆和调整堆的过程中,我们使用 adjustHeap 方法来实现,该方法可以将一个无序的数组构建成一个大根堆,或者调整一个无序的堆,使其满足堆的性质。在 swap 方法中,我们使用了一个临时变量 temp 来交换数组中的两个元素。

五、堆排序的应用场景

堆排序的应用场景包括:

  1. 大规模数据的排序:堆排序的时间复杂度为 O(nlogn),比较适用于大规模数据的排序。在实际应用中,堆排序经常被用来对海量数据进行排序。

  2. 优先队列:堆排序可以用来实现优先队列(Priority Queue),即支持在 O(logn) 时间内插入和删除元素的数据结构。在 Java 中,PriorityQueue 就是使用堆来实现的。

  3. 求最大/最小值:堆排序可以用来求一个集合中的最大或最小值,因为堆的性质可以保证堆顶元素是最大或最小的。

  4. 数据流中的中位数:堆排序可以用来解决数据流中的中位数问题,即如何在不断流入数据的情况下,快速地求出中位数。具体实现方法可以参考数据流中的中位数。

堆排序是一种非常高效的排序算法,可以应用于各种场景中。

六、堆排序在spring 中的应用

Spring 框架中使用堆排序的场景主要是在实现任务调度(Task Scheduling)功能时。Spring 的任务调度模块(Spring Task Scheduling)可以让开发者方便地配置和管理任务调度,支持多种触发器(Trigger)和执行器(Executor),可以满足各种任务调度的需求。

在 Spring 的任务调度模块中,任务调度器(TaskScheduler)使用堆排序来维护任务队列。具体来说,任务调度器会将所有待执行的任务按照触发时间排序,然后按照顺序依次执行。当一个任务执行完毕后,任务调度器会从堆顶取出下一个任务执行,以此类推。

使用堆排序来维护任务队列的好处是可以保证任务按照触发时间的先后顺序执行,同时可以快速地找到下一个要执行的任务。由于堆排序的时间复杂度为 O(nlogn),所以任务调度器可以在高效地管理大量的任务。

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

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

相关文章

5.24 基础题目

快速幂 #include<bits/stdc.h> using namespace std; //126348976 982638476 938420413 int main(){int a,b,p;cin>>a>>b>>p;long long res 1,ci1;int flag0;if(b0){res%p;}else{while(b){if (flag0)cia%p;elseci(ci%p)*(ci%p)%p;if (b&1)res(res…

【学习随笔】

2022/11/13 HTML :讲完了 css&#xff1a;讲完了 作业&#xff1a;编写登陆界面、整理一下sql优化,对于mybatis不熟练的继续练习 关于MySQL优化的问题? 思路总结&#xff1a;主要考虑数据库优化与SQL语句优化。 1&#xff0c;数据库优化&#xff0c;包括存储引擎的优化&…

FreeRTOS:时间管理

目录 前言一、FreeRTOS 延时函数1.1函数vTaskDelay()1.2 函数prvAddCurrentTaskToDelayedList()1.3 函数vTaskDelayUntil() 二、FreeRTOS 系统时钟节拍 前言 在使用FreeRTOS 的过程中我们通常会在一个任务函数中使用延时函数对这个任务延时&#xff0c;当执行延时函数的时候就…

【C/C++】内存管理

【C/C】内存管理 目录 【C/C】内存管理C/C内存分布C语言和C中动态内存管理方式new/delete操作内置类型new和delete操作自定义类型 operator new与operator delete函数operator new与operator delete的类专属重载new和delete的实现原理定位new表达式(placement-new)重要的知识ma…

Docker(1)

一)什么是虚拟化&#xff1f;容器化&#xff1f; 1)物理机:实际的服务器或者计算机&#xff0c;相对于虚拟机而言的对实体计算机的称呼。物理 机提供给虚拟机以硬件环境&#xff0c;有时也称为“寄主”或“宿主&#xff1b; 2)虚拟化:是指通过虚拟化技术将一台计算机虚拟为多台…

【C++】——string类的介绍及模拟实现

文章目录 1. 前言2. string类的常用接口2.1 string类对象的常见构造2.2 string类对象的容量操作2.3 string类对象的访问及遍历操作2.4 string类对象的修改操作2.5 string类非成员函数2.6 string四种迭代器类型2.7 string类的insert和erase函数 3. 浅拷贝和深拷贝4. string类模拟…

评奖系统设计

系列文章 任务40 评奖系统设计 文章目录 系列文章一、实践目的与要求1、目的2、要求 二、课题任务三、总体设计1.存储结构及数据类型定义2.程序结构3.所实现的功能函数4、程序流程图 四、小组成员及分工五、 测试执行完毕程序展示成功&#xff01;学生投票&#xff0c;举例第一…

URP的多Pass和Features用法

回到目录 大家好&#xff0c;我是阿赵。这里用一个传统的描边例子来说明一下&#xff0c;URP下怎么使用多Pass和Features。 一、传统多Pass描边 最常用的制作描边方法&#xff0c;就是写多一个Cull Front的Pass&#xff0c;然后通过法线方向扩展顶点&#xff0c;模拟描边的效…

机试打卡 -05 接雨水(动态规划栈)

我的思路&#xff1a;依次计算每一列能接收的雨水量。 关键点&#xff1a;如何计算得到每一列所能接收到的雨水量&#xff1f; 某一列能够接收到的雨水量&#xff0c;取决于其左右两侧最高的柱子。仅有当左右两侧的柱子均高于该列的高度&#xff0c;该列才可收到雨水&#x…

Java 17 VS Java 8: 新旧对决,这些Java 17新特性你不容错过

&#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; Java是一门非常流行的编程语言&#xff0c;由于其跨平台性、可移植性以及强大的面向对象特性而备受青睐。Java最初由Sun Microsystems公司于1995年推出&#xff0c;随着时间的推…

[极客大挑战 2019]HardSQL1

拿到题目是一个登录界面 提交万能密码后拿到回显信息&#xff0c;说明页面存在过滤 burp抓包爆破后发现&#xff0c;所有736都是被过滤字符 联合注入和时间盲注被过滤&#xff0c;因为页面存在报错信息&#xff0c;所以尝试报错注入 因为空格也被过滤&#xff0c;所以我们使用括…

HOMER docker版本配置优化

概述 HOMER是一款100%开源的针对SIP/VOIP/RTC的抓包工具和监控工具。 HOMER是一款强大的、运营商级、可扩展的数据包和事件捕获系统&#xff0c;是基于HEP/EEP协议的VoIP/RTC监控应用程序&#xff0c;并可以使用即时搜索、处理和存储大量的信令、RTC事件、日志和统计信息。 …

机器学习-01概论

人们在生活中可能已经注意到了这样一种现象&#xff1a;我们能够轻松地通过相貌区分出日本人、韩国人和泰国人&#xff0c;但是面对英国人、俄罗斯人和德国人时&#xff0c;我们却很难辨认他们的面孔。造成这种现象的原因一方面是因为日韩泰都是我国的邻国&#xff0c;我们观察…

信号处理与分析-确定性信号的分析

目录 一、引言 二、确定性信号的定义 三、确定性信号的分类 四、确定性信号的分析方法 4.1 傅里叶变换 4.2 离散傅里叶变换 4.3 离散余弦变换 4.4 小波变换 五、确定性信号的处理方法 六、结论 一、引言 信号分析与处理是现代通信技术和信息处理技术的重要组成部分。…

Redis安装及其配置文件修改

一、redis 安装 点击即可下载 https://download.redis.io/releases/ 将下载后的包通过xftp上传到服务器 解压&#xff0c;我这边是解压到/usr/local目录下 -- 创建路径 mkdir /usr/local/redis -- 解压 tar -zxvf redis-4.0.0.tar.gz -C /usr/local/redis 为防止编译失败&am…

MyBatis-Plus精讲和使用注意事项

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

【国产虚拟仪器】基于Zynq的雷达10Gbps高速PCIE数据采集卡方案(一)总体设计

2.1 引言 本课题是来源于雷达辐射源识别项目&#xff0c;需要对雷达辐射源中频信号进行采集传输 和存储。本章基于项目需求&#xff0c;介绍采集卡的总体设计方案。采集卡设计包括硬件设计 和软件设计。首先对采集卡的性能和指标进行分析&#xff0c;接着提出硬件的总体设计…

详解知识蒸馏原理和代码

目录 知识蒸馏原理概念技巧举例说明KL 散度及损失 KD训练代码导入包网络架构teacher网络student网络 teacher网络训练定义基本函数训练主函数 student网络训练&#xff08;重点&#xff09;理论部分定义kd的loss定义基本函数训练主函数 绘制结果teacher网络的暗知识softmax_t推…

使用dockerfile自定义Tomcat镜像

一&#xff1a;创建目录 mkdir /root/tomcat chmod 777 /root/ chmod 777 /root/tomcat 或者chmod -R 777 /root 这里的无效选项是因为我想递归修改root目录及root目录文件以下的权限 chmod :-R 递归修改指定目录下所有子目录和文件的权限 二&#xff1a;将jdk和apache压…

RPG游戏自动打怪之朝向判断

RPG游戏辅助想要做到自动打怪 获得到最近怪物信息以后 还需要面向怪物 否则背对怪物等等情况是没有办法攻击以及释放技能的 游戏设计的时候朝向是有很多种情况的 第一种 2D&#xff0c;2.5D老游戏&#xff0c;例如传奇 他的朝向一般是极为固定的4朝向或则8朝向 也就是不…