算法系列之排序算法-堆排序

在数据结构中,堆(Heap)是一种特殊的树形数据结构,通常用于实现优先队列。堆分为最大堆和最小堆两种类型。最大堆的每个节点的值都大于或等于其子节点的值,而最小堆的每个节点的值都小于或等于其子节点的值。Java 提供了 PriorityQueue 类来实现堆的功能。

堆的介绍

堆(Heap)是一种满足特定条件的完全二叉树,主要分为两种:如下图所示:

  • 小顶堆:每个节点的值都小于或等于其子节点的值。
  • 大顶堆:每个节点的值都大于或等于其子节点的值。
  1. 特性:
  • 最底层的节点靠左填充,其它层的节点都被填满
  • 根节点称为堆顶,最底层最靠右的节点称为堆底
  • 堆的根节点(堆顶)是堆中最大(大顶堆)或最小(小顶堆)的元素。
  1. 堆的存储

堆通常使用数组来存储。对于一个索引为 i 的节点:

  • 其左子节点的索引为 2i + 1

  • 其右子节点的索引为 2i + 2

  • 其父节点的索引为 (i - 1) / 2

  1. 堆的操作及效率

java中提供的是优先队列(PriorityQueue)

方法名 描述 时间复杂度
add() 元素入堆 O(log n)
poll() 堆顶元素出堆 O(log n)
peek() 访问堆顶元素 O(1)
size() 获取堆中元素的数量 O(1)
isEmpty() 堆是否为空 O(1)

堆排序

堆排序(Heap Sort)是一种基于堆数据结构的比较排序算法。它的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),并且是一种原地排序算法(即不需要额外的存储空间)。堆排序的核心思想是利用堆的性质来维护一个最大堆或最小堆,然后逐步将堆顶元素(最大值或最小值)取出,放到数组的末尾,最终得到一个有序的数组。

堆排序的基本步骤

  1. 构建最大堆

从最后一个非叶子节点开始,向前遍历每个节点,对每个节点执行堆化操作。

最后一个非叶子节点的索引为 n/2 - 1,其中 n 是数组的长度。

  1. 排序

将堆顶元素(最大值)与堆的最后一个元素交换。(出堆)

将堆的大小减一(即忽略最后一个元素)。

对新的堆顶元素执行堆化操作,以维持最大堆的性质。

重复上述步骤,直到堆的大小为1。

代码实现如下:

java 复制代码
/**
 * 堆排序
 */
public class HeapSort {

    public static void heapSort(int[] arr) {
        int n = arr.length;
        //  构建大顶堆
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(arr, n, i);
        }


        // 2. 排序
        for (int i = n - 1; i > 0; i--) {
            // 将堆顶元素(最大值)与当前堆的最后一个元素交换
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;

            // 对新的堆顶元素进行堆化操作
            heapify(arr, i, 0);
        }
    }



    //堆化操作
    public static void heapify(int[] arr,int n,int i){
        int max = i; // 初始化最大值为当前节点
        int left = 2 * i + 1; // 左子节点
        int right = 2 * i + 2; // 右子节点

        // 如果左子节点大于当前最大值,则更新最大值
        if (left < n && arr[left] > arr[max]) {
            max = left;
        }

        // 如果右子节点大于当前最大值,则更新最大值
        if (right < n && arr[right] > arr[max]) {
            max = right;
        }

        // 如果最大值不是当前节点,则交换并继续堆化
        if (max != i) {
            int swap = arr[i];
            arr[i] = arr[max];
            arr[max] = swap;

            // 递归地对子树进行堆化
            heapify(arr, n, max);
        }
    }
    public static void main(String[] args) {
        int[] arr = {8, 10, 7, 4, 6, 2, 5, 1};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

堆排序特性

  • 时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)、非自适应排序

建堆操作适用时间O(n),从堆中提取最大元素时间O(logn),共n-1轮。

  • 空间复杂度为O(1)、原地排序

元素堆化和堆操作都是在原数组上进行的。

  • 非稳定排序

在交换堆顶元素和堆底元素时,相等元素的相对位置可能发生了改变。

优缺点

  1. 优点
  • 时间复杂度较低,适合大规模数据排序。

  • 原地排序,不需要额外的存储空间。

  1. 缺点
  • 不稳定排序算法(相同元素的相对顺序可能改变)。

  • 对于小规模数据,性能不如插入排序等简单算法。

总结

堆排序是一种高效的排序算法,尤其适用于需要原地排序的场景。它的时间复杂度为O(nlogn),并且不需要额外的存储空间。堆排序的核心在于堆化操作,通过维护堆的性质来实现排序。

相关推荐
千千寰宇18 分钟前
[数据库系统] 数据库系统的设计原理研究
数据结构·数据库·database-opengemini/influxdb·database-dament/达梦
聪明蛋子哟26 分钟前
不仅仅是向量检索:结合知识图谱与Tool Calling的混合增强生成(Hybrid RAG)方案
人工智能·算法·知识图谱
Pniubi27 分钟前
力扣55跳跃游戏(贪心)
算法·leetcode·游戏
jyyyx的算法博客1 小时前
传球游戏【思维】
算法
FL16238631291 小时前
关于2026年南昌大学811信号与系统最后一题电路系统解题思路
算法
殷色玫瑰1 小时前
C++ STL:map 与 set 详解——从底层红黑树到实际应用
c语言·数据结构·c++·算法·visualstudio·rpc
行者全栈架构师1 小时前
【鸿蒙心迹】鸿蒙网络请求架构实战——@ohos.net.http 到 Axios 封装、拦截器与统一错误处理(HarmonyOS 7.x)
前端·算法·架构
小宋10211 小时前
OpenTelemetry GenAI可观测性实战:串起模型、工具、Token与错误
java·人工智能·算法·贪心算法
嫂子开门我是_我哥1 小时前
青少年抑郁、焦虑、失眠与自杀相关表现的症状联系:一篇网络分析论文所使用的算法深度解读
论文阅读·算法·论文笔记
字节跳动的猫2 小时前
LikeShop 商品评价体系二开:追评、晒图审核与评价标签筛选功能开发
运维·数据结构·数据库