十大经典排序算法之一--------------堆排序(java详解)

一.堆排序基本介绍:

  1. 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
  2. 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆, 注意 : 没有要求结点的左孩子的值和右孩子的值的大小关系。
  3. 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆

 大顶堆&&小顶堆(图解):

  大顶堆:

 其中,二叉树节点外面标注的是堆对应的数组下标,也就是:

小顶堆:

*假设我们有了一个待排序的数组,并且构建好了他的逻辑结构,怎么能通过孩子找到双亲,或者通过双亲找到左右孩子呢?其实也很好理解,我们拿一颗二叉树出来就能很轻易的得出公式:

具体公式:  parent = (child - 1) / 2 ;

                   leftchild = parent * 2 + 1 ;

                   rightchild = parent * 2 + 2 ;

                   rightchild = leftchild + 1;

 二.堆排序详解:

2.1.堆排序的基本思路(这里以顺序排序为主):

  1. 将待排序序列构造成一个大顶堆
  2. 此时,整个序列的最大值就是堆顶的根节点
  3. 将其与末尾元素进行交换,此时末尾就为最大值
  4. 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

 ①.构建大顶堆:

1).以给定的无序堆为例:

2).此时我们从最后一个非叶子节点开始(叶子节点自然不用调整,最后一个非叶子节点为:

 arr.length / 2 - 1;也就是下面的6节点 ),从左至右,从上至下进行调整:

 3).找到第二个非叶节点4,由于【4,9,8】中9最大,4和9交换:

 4).这时,交换导致了子根【4,5,6】结构混乱(因为我们要建立的是大顶堆),继续调整,【4,5,6】中6最大,交换4和6:

此时,我们就将一个无序序列构造成了一个大顶堆 

建大堆代码实现:

	//将一个数组(二叉树), 调整成一个大顶堆/*** @param arr 待调整的数组* @param i 表示非叶子结点在数组中索引* @param length 表示对多少个元素继续调整, length 是在逐渐的减少*/public  static void adjustHeap(int arr[], int i, int length) {int temp = arr[i];//先取出当前元素的值,保存在临时变量//开始调整//说明//1. k = i * 2 + 1 k 是 i结点的左子结点for(int k = i * 2 + 1; k < length; k = k * 2 + 1) {if(k+1 < length && arr[k] < arr[k+1]) { //说明左子结点的值小于右子结点的值k++; // k 指向右子结点}if(arr[k] > temp) { //如果子结点大于父结点arr[i] = arr[k]; //把较大的值赋给当前结点i = k; //!!! i 指向 k,继续循环比较} else {break;//!}}//当for 循环结束后,我们已经将以i 为父结点的树的最大值,放在了 最顶(局部)arr[i] = temp;//将temp值放到调整后的位置}

②.将堆顶元素与末尾元素进行交换,使末尾元素最大,然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素,如此反复,重建,交换:

1).将堆顶元素9和末尾元素4进行交换:

 2).重新构建堆,使其继续满足堆的定义(除去9的数组建堆):

3).后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序:

 

到这里你想必也能理解为什么是建大堆而不是小堆了吧,这个排序(顺序)过程实际就是利用堆顶的元素最大,使其和最后n-1个元素进行交换(数组的大小是逐渐缩小的),最终使得整个序列有序 

heapSort方法实现:

	//编写一个堆排序的方法public static void heapSort(int arr[]) {int temp = 0;//将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆,这里是建大堆for(int i = arr.length / 2 -1; i >=0; i--) {adjustHeap(arr, i, arr.length);}/** 2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。*/for(int j = arr.length-1;j >0; j--) {//交换temp = arr[j];arr[j] = arr[0];arr[0] = temp;adjustHeap(arr, 0, j); }//System.out.println("数组=" + Arrays.toString(arr)); }

 堆排序完整代码:

import java.util.*;
public class HeapSort {
//测试数据public static void main(String[] args){int[] arr = {4,6,8,5,9};heapSort(arr);System.out.println("堆排序:"+Arrays.toString(arr));}public static void heapSort(int[] arr){int temp = 0;for(int i = arr.length / 2 - 1;i >= 0;i--){adjustHeap(arr,i,arr.length);}for(int j = arr.length - 1;j > 0;j--){temp = arr[j];arr[j] = arr[0];arr[0] = temp;adjustHeap(arr,0,j);}}public static void adjustHeap(int[] arr,int i,int length){int temp = arr[i];for(int k = 2 * i + 1;k < length;k = k * 2 + 1){if(k + 1 < length && arr[k] < arr[k+1]){k++;}if(arr[k] > temp){arr[i] = arr[k];i = k;}else{break;}}arr[i] = temp;}
}

运行结果:

 很明显,排序成功

堆排序速度测试:

 public static void main(String[] args){// 创建要给8000000个的随机的数组int[] arr = new int[8000000];for (int i = 0; i < 8000000; i++) {arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数}System.out.println("排序前");Date data1 = new Date();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String date1Str = simpleDateFormat.format(data1);System.out.println("排序前的时间是=" + date1Str);heapSort(arr);Date data2 = new Date();String date2Str = simpleDateFormat.format(data2);System.out.println("排序前的时间是=" + date2Str);}

 这里给8000000个数组排序,只用了2秒左右,可以看出堆排序的效率是相当的高

小结:

堆排序是一种基于二叉堆数据结构的排序算。它的主要思想是将待排序的元素构建成一个最大堆(或最小堆),然后依次将堆顶元素与堆的最后一个元素交换,并重新调整堆,使得剩余元素仍然满足堆的性质。重复这个过程,直到所有元素都被排序。

堆排序的步骤如下:

  1. 构建最大堆:将待排序的数组看作是一个完全二叉树,从最后一个非叶子节点开始,依次向上调整每个节点,使得每个节点都满足最大堆的性质。
  2. 交换堆顶元素和最后一个元素:将堆顶元素与堆的最后一个元素交换位置,此时最大元素已经排好序。
  3. 调整堆:将剩余元素重新调整为最大堆,再次找到最大元素并交换到堆顶。
  4. 重复步骤2和步骤3,直到所有元素都被排序。

堆排序的时间复杂度为O(nlogn),其中n为待排序数组的长度。它是一种不稳定的排序算法,因为在调整堆的过程中可能会改变相同元素的相对顺序。

博客到这里也是结束了,制作不易,喜欢的小伙伴可以点赞加关注支持下博主,这对我真的很重要~~

 

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

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

相关文章

C语言猜数字小游戏智能版

不仅有对错&#xff0c;还有评分系统&#xff0c;根据回答的次数给予合适的反馈。此代码适合linux和安卓系统&#xff0c;如果windows想用得删除\033[xxm之类的ANSI打印转义字符&#xff0c;换成windows对应的清屏、切换颜色函数 #include<stdio.h> #include <stdlib…

LTE-M与NB-IoT蜂窝物联网

大规模、高速连接的部署需要大量带宽的支持。而蜂窝低功耗广域 (LPWA)技术为传感器和短数据突发交易设备在快速的、以 5G 为中心的技术世界中的不间断运行让路。长期演进机器类型通信 (LTE-M) 和窄带物联网 (NB-IoT)等移动物联网技术提供安全且经济高效的蜂窝 LPWA 功能。它们是…

内存基础知识

内存作用&#xff1a;用来存放数据 int x10&#xff1b; xx1&#xff1b; 这会生成一个可执行文件&#xff08;装入模块&#xff09;然后存入内存地址中 绝对装入&#xff1a;-如果知道程序放到内存中哪个位置&#xff0c;编译程序将产生绝对地址的目标代码 可重定位装入&am…

汽车零部件软件开发中常用滤波算法

滑动窗口滤波器:均值滤波与中值滤波的应用及局限性 滑动窗口滤波是数字信号处理中的基本技术,通过在数据序列上移动一个固定大小的窗口并计算窗口内数据点的统计量(如均值或中值)来平滑信号。本文将探讨滑动窗口均值滤波和中值滤波的基本实现、工作原理及其局限性,并引入…

idea 打不开项目 白屏

使用IDEA打开项目&#xff0c; 不知名原因崩溃了&#xff0c; 直接出现缩略图白屏。 解决过程&#xff1a; 尝试过重启IDEA&#xff0c;重启过电脑&#xff0c;重新引入相同项目&#xff08;使用不同路径&#xff0c;存在缓存记录&#xff0c;依然打不开&#xff09;&#xff…

数据结构——lesson3单链表介绍及实现

目录 1.什么是链表&#xff1f; 2.链表的分类 &#xff08;1&#xff09;无头单向非循环链表&#xff1a; &#xff08;2&#xff09;带头双向循环链表&#xff1a; 3.单链表的实现 &#xff08;1&#xff09;单链表的定义 &#xff08;2&#xff09;动态创建节点 &#…

删除链表的倒数第N个节点

删除链表的倒数第N个节点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 进阶&#xff1a;你能尝试使用一趟扫描实现吗&#xff1f; 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例…

蓝桥杯Java组备赛(二)

题目1 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int max Integer.MIN_VALUE;int min Integer.MAX_VALUE;double sum 0;for(int i0;i<n;i) {int x sc.nextInt()…

13.5. 多尺度目标检测

这里是对那一节代码的通俗注释&#xff0c;希望对各位学习有帮助。 值得注意的是&#xff0c;multibox_prior函数的宽高计算网络上有争议&#xff0c;此处我仍认为作者的写法是正确的&#xff0c;如果读者有想法&#xff0c;可以在评论区留言&#xff0c;我们进行讨论。 import…

文件上传漏洞--Upload-labs--Pass03--特殊后缀与::$DATA绕过

方法一&#xff1a;特殊后缀绕过&#xff1a; 一、什么是特殊后缀绕过 源代码中的黑名单禁止一系列后缀名 之外的后缀&#xff0c;称之为‘特殊后缀名’&#xff0c;利用其来绕过黑名单&#xff0c;达到上传含有恶意代码的文件的目的。 二、代码审计 接下来对代码逐条拆解进行…

iocp简单例子

首先说明&#xff1a;纯iocp使用的例子看&#xff1a;纯iocp例子&#xff08;里面的代码可能无法运行&#xff0c;但是下面的代码一定可以运行&#xff0c;可以看看它里面的 PostQueuedCompletionStatus函数的使用&#xff0c;参考参考然后拿出来放到下面的代码里测试&#xff…

VQ23 请按城市对客户进行排序,如果城市为空,则按国家排序(order by和case when的连用)

代码 select * from customers_info order by (case when city is null then country else city end)知识点 order by和case when的连用

VQ30 广告点击的高峰期(order by和limit的连用)

代码 select hour(click_time) as click_hour ,count(hour(click_time)) as click_cnt from user_ad_click_time group by click_hour order by click_cnt desc limit 1知识点 order by和limit的连用&#xff0c;取出所需结果 YEAR() 返回统计的年份 MONTH() 返回统计的月份 D…

解决Ubuntu下网络适配器桥接模式下ping网址不通的情况

问题反应&#xff1a;ping不通网址 打开虚拟机中的设置&#xff0c;更改网络适配器为NAT模式 确定保存更改之后&#xff0c;退出输入如下命令。 命令1&#xff1a; sudo /etc/network/inferfaces 命令2&#xff1a; sudo /etc/init.d/network/ restart

《生产调度优化》专栏导读

文章分类 生产调度优化问题入门相关问题求解调度问题求解效率探讨相关论文解读 生产调度优化问题入门 文章包含重点简述生产车间调度优化问题两种常用的FJSP模型解析FJSP问题的标准测试数据集的Python代码解析FJSP标准测试数据代码 相关问题求解 文章求解器问题类型【作业车…

使用 C++23 从零实现 RISC-V 模拟器(5):CSR

&#x1f449;&#x1f3fb; 文章汇总「从零实现模拟器、操作系统、数据库、编译器…」&#xff1a;https://okaitserrj.feishu.cn/docx/R4tCdkEbsoFGnuxbho4cgW2Yntc RISC-V为每个hart定义了一个独立的控制状态寄存器&#xff08;CSR&#xff09;地址空间&#xff0c;提供了4…

小程序列表下拉刷新和加载更多

配置 在小程序的app.json中&#xff0c;检查window项目中是否已经加入了"enablePullDownRefresh": true&#xff0c;这个用来开启下拉刷新 "window": {"backgroundTextStyle": "light","navigationBarBackgroundColor": &q…

unity C#中的封装、继承和多态简单易懂的经典实例

文章目录 封装 (Encapsulation)继承 (Inheritance)多态 (Polymorphism) C#中的封装、继承和多态是面向对象编程&#xff08;OOP&#xff09;的三大核心特性。下面分别对这三个概念进行深入解释&#xff0c;并通过实例来说明它们在实际开发中的应用。 封装 (Encapsulation) 实例…

【北京航空航天大学】【信息网络安全实验】【实验一、密码学:DES+RSA+MD5编程实验】

信息网络安全实验 实验一、DES RSA MD5 一、实验目的 1. 通过对DES算法的代码编写,了解分组密码算法的设计思想和分组密码算法工作模式; 2. 掌握RSA算法的基本原理以及素数判定中的Rabin-Miller测试原理、Montgomery快速模乘(模幂)算法,了解公钥加密体制的优缺点及其常…

gem5学习(21):索引策略——Indexing Policies

目录 一、集合关联&#xff08;Set Associative&#xff09; 二、倾斜关联&#xff08;Skewed Associative&#xff09; 索引策略确定基于地址将一个块映射到哪个位置。 索引策略的最重要方法是getPossibleEntries()和regenerateAddr()&#xff1a; getPossibleEntries()用…