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

算法笔记之堆排序

本系列可作为算法学习系列的笔记,小编会将代码记录下来,大家复制下来就可以练习了,方便大家学习。小编作为新晋码农一枚,会定期整理一些写的比较好的代码,作为自己的学习笔记,会试着做一下批注和补充,如转载或者参考他人文献会标明出处,非商用,如有侵权会删改!欢迎大家斧正和讨论!

系列文章目录 

计算矩阵的鞍点个数

算法-比较排序

为什么比较排序算法的时间复杂度下界是 Ω(n log n)?  

算法笔记之堆排序 


一. 堆排序(Heap Sort)

堆排序是一种高效的排序算法,它利用了二叉堆这种数据结构来实现。堆排序的时间复杂度为O(nlogn),且是原地排序算法(不需要额外的存储空间)。

1.堆的基本概念

堆是一种特殊的完全二叉树,满足以下性质:

  • 最大堆:每个节点的值都大于或等于其子节点的值
  • 最小堆:每个节点的值都小于或等于其子节点的值

在堆排序中,我们通常使用最大堆。

2.堆排序的基本步骤

  1. 建堆(Heapify):将无序数组构建成一个堆
  2. 排序:
    • 将堆顶元素(最大值)与最后一个元素交换
    • 缩小堆的范围(排除最后一个元素)
    • 对新的堆顶元素进行堆调整(Heap Adjust)
    • 重复上述过程直到堆的大小为1

3.C语言实现代码

#include <stdio.h>// 交换两个元素的值
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}// 堆调整函数(下沉操作)
// arr: 待调整的数组
// n: 数组长度
// i: 当前需要调整的节点下标
void heapAdjust(int arr[], int n, int i) {int largest = i;        // 初始化最大值为当前节点int left = 2 * i + 1;   // 左子节点int right = 2 * i + 2;  // 右子节点// 如果左子节点存在且大于当前最大值if (left < n && arr[left] > arr[largest]) {largest = left;}// 如果右子节点存在且大于当前最大值if (right < n && arr[right] > arr[largest]) {largest = right;}// 如果最大值不是当前节点,交换并继续调整if (largest != i) {swap(&arr[i], &arr[largest]);heapAdjust(arr, n, largest);  // 递归调整受影响的子树}
}// 建堆函数
void buildHeap(int arr[], int n) {// 从最后一个非叶子节点开始,向上调整for (int i = n / 2 - 1; i >= 0; i--) {heapAdjust(arr, n, i);}
}// 堆排序函数
void heapSort(int arr[], int n) {// 1. 构建最大堆buildHeap(arr, n);// 2. 逐个提取元素for (int i = n - 1; i > 0; i--) {// 将当前最大值(堆顶)移动到数组末尾swap(&arr[0], &arr[i]);// 对剩余元素重新调整堆heapAdjust(arr, i, 0);}
}// 测试代码
int main() {int arr[] = {12, 11, 13, 5, 6, 7};int n = sizeof(arr) / sizeof(arr[0]);printf("原始数组: \n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");heapSort(arr, n);printf("排序后数组: \n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}

4.实现逻辑详解

1. 堆调整(heapAdjust)

堆调整(也称为"下沉"操作)是堆排序的核心操作,它确保以某个节点为根的子树满足堆的性质。

  • ​参数​​:

    • arr: 待调整的数组
    • n: 当前堆的大小
    • i: 需要调整的节点索引
  • ​步骤​​:

    1. 找到当前节点、左子节点和右子节点中的最大值
    2. 如果最大值不是当前节点,交换它们
    3. 递归调整受影响的子树

2. 建堆(buildHeap)

建堆是将一个无序数组转换为堆的过程。

  • ​关键点​​:

    • 从最后一个非叶子节点开始(索引为n/2-1)向前调整
    • 这样能确保每次调整时,子树已经是堆
  • ​为什么从n/2-1开始​​:

    • 因为叶子节点本身已经是堆(没有子节点)
    • 最后一个非叶子节点是第一个可能有子节点的节点

3. 堆排序(heapSort)

堆排序的主函数,完成整个排序过程。

  • ​步骤​​:
    1. 首先构建最大堆
    2. 然后重复以下操作直到堆大小为1:
      • 将堆顶元素(最大值)与当前堆的最后一个元素交换
      • 堆大小减1
      • 对新的堆顶元素进行堆调整

5.时间复杂度分析

  • ​建堆​​:O(n) - 看似是O(nlogn),但实际计算是O(n)
  • ​排序阶段​​:O(nlogn) - 每次堆调整是O(logn),共n-1次
  • ​总体​​:O(nlogn)

堆排序是原地排序,不需要额外的存储空间(除了递归调用栈,可以改为迭代实现来避免)。

6.总结

堆排序是一种高效的排序算法,特别适合处理大规模数据。它的主要优势在于最坏情况下也能保持O(nlogn)的时间复杂度,并且是原地排序。理解堆排序的关键在于掌握堆的调整和建堆过程。

http://www.dtcms.com/a/292395.html

相关文章:

  • Oracle数据恢复—Oracle数据库所在分区被删除后报错的数据恢复案例
  • Oracle 12c 创建数据库初级教程
  • sqli-labs通关笔记-第14关 POST报错型注入(双引号闭合 手工注入+脚本注入两种方法)
  • mac实现sudo命切换node版本
  • 【C++进阶】揭秘list迭代器:从底层实现到极致优化
  • WIFI路由器长期不重启,手机连接时提示无IP分配
  • 【Linux系统】基础IO
  • Git使用git graph插件回滚版本
  • 【自定义一个简单的CNN模型】——深度学习.卷积神经网络
  • 大气能见度监测仪:洞察大气 “清晰度” 的科技之眼
  • 智慧教室:科技赋能,奏响个性化学习新乐章
  • MyBatis拦截器插件:实现敏感数据字段加解密
  • 中国科技信息杂志中国科技信息杂志社中国科技信息编辑部2025年第14期目录
  • 「芯生态」杰发科技AC7870携手IAR开发工具链,助推汽车电子全栈全域智能化落地
  • Vue中最简单的PDF引入方法及优缺点分析
  • docker build 和compose 学习笔记
  • CASB架构:了解正向代理、反向代理和API扫描
  • [转]Rust:过程宏
  • JMeter 实现 Protobuf 加密解密
  • AI 音频产品开发模板及流程(一)
  • 网络安全第三次作业搭建前端页面并解析
  • allegro 16.6配置CIS库报错 ORCIS-6129 ORCIS-6469
  • LeetCode 658.找到K个最接近的元素
  • .NET使用EPPlus导出EXCEL的接口中,文件流缺少文件名信息
  • Unity笔记——事件中心
  • 力扣-300.最长递增子序列
  • 以太坊网络发展分析:技术升级与市场动态的双重驱动
  • 快手开源 Kwaipilot-AutoThink 思考模型,有效解决过度思考问题
  • Cy3-COOH 花菁染料Cy3-羧基
  • linux-日志服务