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

堆排序算法详解:原理、实现与C语言代码

堆排序(Heap Sort)是一种高效的排序算法,利用二叉堆数据结构实现。其核心思想是将待排序序列构造成一个大顶堆(或小顶堆),通过反复调整堆结构完成排序。下面从原理到实现进行详细解析。


一、核心概念:二叉堆

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

  • 大顶堆:父节点值 ≥ 子节点值(用于升序排序)

  • 小顶堆:父节点值 ≤ 子节点值(用于降序排序)


二、堆排序步骤
  1. 初始建堆:将无序序列构建成大顶堆

  2. 堆排序:反复将堆顶元素与末尾交换,并调整堆

1. 初始建堆(Heapify)

从最后一个非叶子节点开始,自底向上调整堆结构。

关键公式(数组下标从0开始):

  • 节点 i 的左子节点:2i + 1

  • 节点 i 的右子节点:2i + 2

  • 最后一个非叶子节点:n/2 - 1

调整过程

  1. 比较当前节点与左右子节点

  2. 若子节点更大,则与最大子节点交换

  3. 递归调整被影响的子树

2. 堆排序流程
  1. 将堆顶元素(最大值)与末尾元素交换

  2. 堆大小减1,重新调整堆顶元素

  3. 重复直到堆大小为1


三、C语言完整实现
#include <stdio.h>// 交换元素
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}// 调整堆(大顶堆)
void heapify(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]);heapify(arr, n, largest);  // 递归调整受影响子树}
}// 堆排序主函数
void heapSort(int arr[], int n) {// 初始建堆:从最后一个非叶子节点开始for (int i = n / 2 - 1; i >= 0; i--)heapify(arr, n, i);// 依次提取堆顶元素for (int i = n - 1; i > 0; i--) {swap(&arr[0], &arr[i]);  // 将堆顶移至末尾heapify(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]);heapSort(arr, n);printf("\n排序后数组:\n");for (int i = 0; i < n; i++)printf("%d ", arr[i]);return 0;
}

四、算法分析
指标说明
时间复杂度O(n log n)建堆O(n),每次调整O(log n)
空间复杂度O(1)原地排序
稳定性不稳定交换可能改变相等元素顺序
适用场景大数据量排序内存受限时优于归并排序

五、关键点解析
  1. 为什么从n/2-1开始建堆?
    完全二叉树中,非叶子节点占总数一半,且最后一个非叶子节点的下标为n/2-1

  2. 为何堆排序不稳定?
    交换堆顶与末尾元素时,可能破坏相等元素的原始顺序(如[5, 5, 3])。

  3. 优化方向

    • 小规模数据用插入排序优化

    • 循环展开加速堆调整

    • 多线程并行处理子树调整

堆排序是高效排序算法的基石,也是优先级队列的核心实现。理解其原理对掌握高级数据结构至关重要。

http://www.xdnf.cn/news/15518.html

相关文章:

  • Opencv---cv::minMaxLoc函数
  • 激活函数LeakyReLU
  • ai 编程工具,简单总结
  • C++设计模式之创建型模式
  • WSL2更新后Ubuntu 24.04打不开(终端卡住,没有输出)
  • Java对象的比较
  • 每日算法刷题Day49:7.16:leetcode 差分5道题,用时2h
  • 什么是数据仓库?数据库与数据仓库有什么关系?
  • 格密码--Ring-SIS和Ring-LWE
  • Python 日志轮换处理器的参数详解
  • 【python】sys.executable、sys.argv、Path(__file__) 在PyInstaller打包前后的区别
  • C语言:第07天笔记
  • smolagents - 如何在mac用agents做简单算术题
  • STM32外设介绍3:(UART 和 USART 通信详解<含重定向与 DMA>)
  • 大端序与小端序
  • 【机器学习】数据理解:数据导入、数据审查与数据可视化
  • 2.库操作
  • 自动驾驶激光3D点云处理系统性阐述及Open3D库函数应用
  • 百炼Agent MCP与IoT实战(二):阿里云MQTT Broker配置
  • 控制Vue对话框显示隐藏
  • fastadmin会员单点登录
  • python的慈善捐赠平台管理信息系统
  • MyBatis详解以及在IDEA中的开发
  • 数据结构与算法学习(一)
  • 个人笔记(linux/tr命令)
  • LVS:高性能负载均衡利器
  • Java的Gradle项目,使用SLF4J+Log4j2+log4j2.xml
  • C++ 计数排序、归并排序、快速排序
  • Elasticsearch+Logstash+Filebeat+Kibana部署
  • xss作业