当前位置: 首页 > news >正文

数据结构第22节 堆排序优化

堆排序是一种基于比较的排序算法,其核心是使用一种叫做“堆”的数据结构。堆通常是一个完全二叉树的数组表示,在数组中可以快速找到最大值或最小值。堆排序可以分为两种类型:最大堆和最小堆。

在最大堆中,父节点的键始终大于或等于任何子节点的键。在最小堆中,情况则相反,父节点的键始终小于或等于任何子节点的键。堆排序通常使用最大堆进行降序排序,或者使用最小堆进行升序排序。

以下是使用Java实现堆排序的基本步骤:

  1. 构建最大堆

    • 将输入数组视为一个完全二叉树的数组表示。
    • 从最后一个非叶子节点开始(即array.length / 2 - 1),对每个节点应用下沉操作,以确保其满足最大堆的性质。
  2. 排序

    • 将堆顶元素(最大值)与堆末尾的元素交换,减少堆的大小,并将新的堆顶元素下沉,使其再次满足最大堆的性质。
    • 重复此过程,直到堆的大小减少到1。

下面是一个具体的Java代码示例来实现堆排序:

public class HeapSort {public static void heapSort(int[] array) {// Build heap (rearrange array)for (int i = array.length / 2 - 1; i >= 0; i--) {heapify(array, array.length, i);}// One by one extract an element from heapfor (int i = array.length - 1; i > 0; i--) {// Move current root to endint temp = array[0];array[0] = array[i];array[i] = temp;// call max heapify on the reduced heapheapify(array, i, 0);}}// To heapify a subtree rooted with node i which is an index in array[]private static void heapify(int[] array, int heapSize, int i) {int largest = i; // Initialize largest as rootint leftChild = 2 * i + 1; // left childint rightChild = 2 * i + 2; // right child// If left child is larger than rootif (leftChild < heapSize && array[leftChild] > array[largest]) {largest = leftChild;}// If right child is larger than largest so farif (rightChild < heapSize && array[rightChild] > array[largest]) {largest = rightChild;}// If largest is not rootif (largest != i) {int swap = array[i];array[i] = array[largest];array[largest] = swap;// Recursively heapify the affected sub-treeheapify(array, heapSize, largest);}}
}

在这个例子中,heapSort函数首先通过调用heapify函数建立一个最大堆,然后将堆顶元素与堆的最后一个元素交换,减少堆的大小,并重新调整剩余元素以保持最大堆的性质。这样重复直到整个数组排序完成。

为了进一步优化堆排序的代码,我们可以考虑以下几点:

  1. 内联小函数:如果heapify函数很小且频繁调用,可以考虑将其内联到heapSort函数中,以减少函数调用的开销。

  2. 减少数组访问:在heapify函数中,多次访问array中的元素可能会导致缓存未命中,从而影响性能。我们可以通过缓存一些频繁访问的值来减少这种影响。

  3. 边界条件检查:确保在循环中检查边界条件,避免不必要的计算。

  4. 使用循环展开:对于某些循环,使用循环展开可以减少循环控制结构的开销。

  5. 尾递归优化:如果使用了递归的heapify,可以尝试将其转换为迭代版本,因为JVM可能不支持尾递归优化。

下面是一个经过微调的堆排序实现,其中heapify函数被内联并进行了数组访问优化:

public class HeapSortOptimized {public static void heapSort(int[] array) {int n = array.length;// Build heapfor (int i = n / 2 - 1; i >= 0; i--) {int largest = i;int leftChild = 2 * i + 1;int rightChild = 2 * i + 2;if (leftChild < n && array[leftChild] > array[largest]) {largest = leftChild;}if (rightChild < n && array[rightChild] > array[largest]) {largest = rightChild;}if (largest != i) {int temp = array[i];array[i] = array[largest];array[largest] = temp;// Recursively heapify the affected sub-treei = largest;leftChild = 2 * i + 1;rightChild = 2 * i + 2;while (leftChild < n) {largest = i;if (array[leftChild] > array[largest]) {largest = leftChild;}if (rightChild < n && array[rightChild] > array[largest]) {largest = rightChild;}if (largest == i) {break;}temp = array[i];array[i] = array[largest];array[largest] = temp;i = largest;leftChild = 2 * i + 1;rightChild = 2 * i + 2;}}}// Extract elements one by onefor (int i = n - 1; i > 0; i--) {int temp = array[0];array[0] = array[i];array[i] = temp;int j = 0;int leftChild = 1;int rightChild = 2;while (leftChild < i) {int largest = j;if (array[leftChild] > array[largest]) {largest = leftChild;}if (rightChild < i && array[rightChild] > array[largest]) {largest = rightChild;}if (largest == j) {break;}temp = array[j];array[j] = array[largest];array[largest] = temp;j = largest;leftChild = 2 * j + 1;rightChild = 2 * j + 2;}}}
}

这个优化版本中,heapify逻辑被直接整合到了heapSort函数中,并且在每次交换后都检查是否需要继续下沉,以避免不必要的额外工作。此外,数组元素的频繁访问被优化,减少了不必要的内存读取。但是,值得注意的是,过度优化可能会影响代码的可读性和维护性,因此在实际开发中需要权衡利弊。

堆排序的过程可以分为两个主要阶段:构建最大堆和排序。我将以一个简单的示例数组 [4, 10, 3, 5, 1] 来逐步说明堆排序的过程,并使用表格形式展示每一步的变化。

第一阶段:构建最大堆

  1. 初始状态:

    | Index | Value |
    |-------|-------|
    |   0   |   4   |
    |   1   |  10   |
    |   2   |   3   |
    |   3   |   5   |
    |   4   |   1   |
    
  2. 从最后一个非叶节点开始下沉:

    • 节点 1 (值 10) 是最后一个非叶节点。
    • 节点 1 已经是最大堆的一部分,无需调整。
  3. 节点 0 下沉:

    • 节点 0 (值 4) 有子节点 1 (值 10) 和 2 (值 3),其中最大的是子节点 1。
    • 交换节点 0 和节点 1 的值。
    | Index | Value |
    |-------|-------|
    |   0   |  10   |
    |   1   |   4   |
    |   2   |   3   |
    |   3   |   5   |
    |   4   |   1   |
    
    • 节点 0 现在是值 10,它没有子节点比它大,所以不需要进一步调整。

现在,我们有一个最大堆:

| Index | Value |
|-------|-------|
|   0   |  10   |
|   1   |   4   |
|   2   |   3   |
|   3   |   5   |
|   4   |   1   |

第二阶段:排序

  1. 交换堆顶元素与最后一个元素:

    • 交换节点 0 (值 10) 和节点 4 (值 1)。
    | Index | Value |
    |-------|-------|
    |   0   |   1   |
    |   1   |   4   |
    |   2   |   3   |
    |   3   |   5   |
    |   4   |  10   |
    
    • 减少堆的大小,不再考虑索引 4。
  2. 调整堆:

    • 节点 0 (值 1) 需要下沉。
    • 交换节点 0 和节点 3 (值 5)。
    | Index | Value |
    |-------|-------|
    |   0   |   5   |
    |   1   |   4   |
    |   2   |   3   |
    |   3   |   1   |
    |   4   |  10   |
    
    • 节点 0 现在是值 5,它没有子节点比它大,所以不需要进一步调整。
  3. 再次交换堆顶元素与最后一个元素:

    • 交换节点 0 (值 5) 和节点 3 (值 1)。
    | Index | Value |
    |-------|-------|
    |   0   |   1   |
    |   1   |   4   |
    |   2   |   3   |
    |   3   |   5   |
    |   4   |  10   |
    
    • 减少堆的大小,不再考虑索引 3。
  4. 调整堆:

    • 节点 0 (值 1) 需要下沉。
    • 由于节点 0 没有子节点,不需要进一步调整。

重复以上步骤,直到堆的大小减至 1。

最终排序结果:

| Index | Value |
|-------|-------|
|   0   |   1   |
|   1   |   3   |
|   2   |   4   |
|   3   |   5   |
|   4   |  10   |

这就是堆排序的完整过程,通过构建最大堆和不断交换并调整堆,最后得到一个有序的数组。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Hive的分区表分桶表
  • RKNN3588——利用推理YOLOv8推理图片
  • 浅析Nginx技术:开源高性能Web服务器与反向代理
  • [RK3566-Android11] 使用iPhone14/15出现的蓝牙断开重连无声音问题
  • duplicate key value violates unique constraint
  • 谷粒商城学习笔记-19-快速开发-逆向生成所有微服务基本CRUD代码
  • 科研绘图系列:R语言两组数据散点分布图(scatter plot)
  • 【Java16】多态
  • 【Cesium开发实战】火灾疏散功能的实现,可设置火源点、疏散路径、疏散人数
  • 修正版头像上传组件
  • 网络规划与设计————期末复习
  • 华为手机联系人不见了怎么恢复?3个解决方案
  • Go协程与通道的综合应用问题
  • 240707-Sphinx配置Pydata-Sphinx-Theme
  • Linux tputs
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • android图片蒙层
  • CODING 缺陷管理功能正式开始公测
  • JavaScript中的对象个人分享
  • Map集合、散列表、红黑树介绍
  • mysql常用命令汇总
  • python 装饰器(一)
  • Redis学习笔记 - pipline(流水线、管道)
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 精彩代码 vue.js
  • 面试总结JavaScript篇
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 无服务器化是企业 IT 架构的未来吗?
  • 消息队列系列二(IOT中消息队列的应用)
  • 责任链模式的两种实现
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 7行Python代码的人脸识别
  • ionic异常记录
  • 智能情侣枕Pillow Talk,倾听彼此的心跳
  • ​数据链路层——流量控制可靠传输机制 ​
  • # 飞书APP集成平台-数字化落地
  • ###C语言程序设计-----C语言学习(6)#
  • #define MODIFY_REG(REG, CLEARMASK, SETMASK)
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (二)fiber的基本认识
  • (附源码)php新闻发布平台 毕业设计 141646
  • (含笔试题)深度解析数据在内存中的存储
  • (蓝桥杯每日一题)love
  • (三)elasticsearch 源码之启动流程分析
  • (算法)大数的进制转换
  • (文章复现)基于主从博弈的售电商多元零售套餐设计与多级市场购电策略
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • (转)h264中avc和flv数据的解析
  • (转)编辑寄语:因为爱心,所以美丽
  • (转)负载均衡,回话保持,cookie
  • *1 计算机基础和操作系统基础及几大协议