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

Java 堆排序(Heap Sort)详解教程

文章目录

  • Java 堆排序(Heap Sort)详解教程
    • 一、什么是堆(Heap)?
    • 二、堆的数组存储结构
      • 示例:数组 [4, 6, 8, 5, 9]
    • 三、堆排序核心思路
      • 核心思想一句话:
    • 四、为什么从 `n/2 - 1` 开始建堆?
      • 数学解释:
      • 举例验证:
      • 直觉理解:
    • 五、为什么不能从根节点(0)开始建堆?
    • 六、堆化(heapify)的本质
      • 伪代码逻辑:
    • 七、完整堆排序代码
    • 八、为什么要“依次取出堆顶并重建堆”?
    • 九、时间复杂度分析
    • 十、完整流程图
    • 十一、总结

Java 堆排序(Heap Sort)详解教程

一、什么是堆(Heap)?

堆是一种完全二叉树(complete binary tree),分为:

  • 大顶堆(max heap):每个节点的值 ≥ 子节点。
  • 小顶堆(min heap):每个节点的值 ≤ 子节点。

在堆排序中,我们使用 大顶堆 来进行 升序排序


二、堆的数组存储结构

堆通常不用链表存储,而是直接放在数组里。
节点下标关系如下(假设数组下标从 0 开始):

节点公式说明
父节点i当前节点索引
左子节点2*i + 1
右子节点2*i + 2

示例:数组 [4, 6, 8, 5, 9]

树形结构如下:

           4(0)/        \6(1)         8(2)/     \5(3)    9(4)

三、堆排序核心思路

核心思想一句话:

不断取出堆顶(最大值),放到数组末尾,然后重建大顶堆。

也就是:

  1. 先把数组调整成「大顶堆」;
  2. 交换堆顶元素(最大值)与数组末尾;
  3. 堆大小减 1,重新调整为大顶堆;
  4. 重复直到数组有序。

四、为什么从 n/2 - 1 开始建堆?

这个是很多人疑惑的地方👇

数学解释:

对于数组长度 n

  • 叶子节点的下标范围是 [n/2, n-1]

  • 因为在堆的结构中:

    左孩子 = 2*i + 1
    

    所以只要 2*i + 1 < ni 就是一个非叶子节点。

推导:

2*i + 1 < n
→ i < (n - 1)/2
→ i_max = floor((n - 1)/2)

因此:

最后一个非叶子节点的下标 = n/2 - 1。

举例验证:

数组长度 n = 5
→ 最后一个非叶子节点 = 5/2 - 1 = 1
(节点索引 1:左=3, 右=4 ✅)

直觉理解:

因为从 n/2 开始的节点,它们都已经是叶子,没有孩子要调整。

所以建堆时:

for (int i = n/2 - 1; i >= 0; i--) {heapify(arr, n, i);
}

从下往上建堆,子树先调整好,父节点才能正确“下沉”。
这就回答了你的另一个疑问👇


五、为什么不能从根节点(0)开始建堆?

因为如果你从上往下建:

  • 根节点下沉时会依赖子节点的堆结构;
  • 但此时子节点可能还没被调整好。

比如:

      4/   \9     8

当根节点 4 尝试下沉时,无法正确判断应该下沉到哪里,因为左右子树还没整理成堆。

所以必须从最后一个非叶子节点开始,自底向上堆化。


六、堆化(heapify)的本质

堆化的作用是:

让以当前节点 i 为根的子树满足大顶堆性质。

伪代码逻辑:

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, largest);heapify(arr, n, largest);}
}

⚙️ 注意:heapify 是递归的。
它会让当前节点「下沉」到正确位置,直到子树满足大顶堆。


七、完整堆排序代码

public class HeapSort {public static void heapSort(int[] arr) {int n = arr.length;// 1️⃣ 构建初始大顶堆for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}// 2️⃣ 依次取出堆顶(最大值),放到数组末尾for (int i = n - 1; i > 0; i--) {swap(arr, 0, i);        // 最大值移到末尾heapify(arr, i, 0);     // 重新堆化剩余部分}}private static 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, largest);heapify(arr, n, largest);}}private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}// 测试public static void main(String[] args) {int[] arr = {4, 6, 8, 5, 9};heapSort(arr);System.out.println(Arrays.toString(arr)); // [4, 5, 6, 8, 9]}
}

八、为什么要“依次取出堆顶并重建堆”?

因为堆顶元素是整个堆中最大的。

  1. 把堆顶和末尾元素交换 → 最大元素放到数组末尾;
  2. 缩小堆的范围(排除最后一个最大值);
  3. 重建堆(保持剩下的部分仍然是大顶堆)。

如此反复,每次都确定一个新的“最大值”,放到正确位置,最终数组有序。


九、时间复杂度分析

阶段操作复杂度
建堆从底向上 heapifyO(n)
排序取堆顶 + 重建堆 n 次O(n log n)
总体O(n log n)

空间复杂度:O(1),属于原地排序。


十、完整流程图

输入数组 → [4,6,8,5,9]
↓
建堆
↓
[9,6,8,5,4]
↓
交换堆顶与末尾 → [4,6,8,5,9]
↓
重建堆 → [8,6,4,5,9]
↓
重复…
↓
最终有序:[4,5,6,8,9]

十一、总结

步骤说明核心
1n/2 - 1 开始建堆因为这是最后一个非叶子节点
2从下往上建堆确保子树先堆化
3每次交换堆顶和末尾把当前最大元素放对位置
4重建堆保证剩余部分仍是大顶堆
5重复 n 次数组排序完成
http://www.dtcms.com/a/520296.html

相关文章:

  • 软件设计师知识点总结:操作系统
  • 黄岩路桥网站设计网站流量提升方案
  • 设计师网站欣赏店铺只做商品展示网站怎么做
  • dify部署及SSL自签实现
  • 云南省建设厅标准员网站手机兼职赚钱
  • Redis哈希表渐进式rehash深度解析:为何百万数据迁移不阻塞服务?
  • 广东省省考备考(第一百三十一天10.23)——科学推理:电学(第六节课)
  • Spring的三级缓存和SpringMVC的流程
  • 为什么麒麟信创系统需要开启overcommit_memory才能安装postgresql成功
  • PostGresql All语法
  • [java] 图文示八股
  • 【图像处理】图像形态学操作
  • 网站上传 空间 数据库开发一个电商平台app要多少钱
  • 如何制作网站链接数字镭网站开发
  • 使用python的matplotlib进行绘图
  • Nginx使用auth_request模块做外部认证集成Kibana
  • 【题解】洛谷 P2218 [HAOI2007] 覆盖问题 [二分 + 思维]
  • xss-labs pass-12
  • 企业网站建设服务电话做网站什么主题好做
  • 注册电气工程师(供配电)执业资格考试专业考试规范及设计手册(2025版)
  • 关于zwg技术的深度解析与应用前景
  • linux 什么做网站好网站优化课程培训
  • 键盘PCB为何对板厂要求更高?差异、难点及猎板解决方案解析
  • OMSDK WebView Display 接入步骤
  • 零基础新手小白快速了解掌握服务集群与自动化运维(十S四)储存服务-NFS文件储存
  • tidex-数字货币交易所
  • C#使用OpenVinoSharp+魔塔社区的读光中英文OCR ONNX模型进行文字检测(仅检测不做识别)
  • 积分商城小程序深圳seo网络优化公司
  • [Linux文件系统——Lesson17.软硬链接]
  • apr库在x86架构下交叉编译成arm64架构