【十大排序算法】堆排序

在堆的古枝上,数字舞动如风,交换岁月,序列重塑成升腾的旋律。

一、堆排序

堆排序是一种基于比较的排序算法,它使用了数据结构“堆”。堆排序可以被看作是一种改进的选择排序,它的工作原理是将待排序的序列构造成一个大顶堆或小顶堆,然后移除堆顶元素并放在序列的末尾,再调整剩余元素使之保持堆的性质,如此反复直到所有元素都被排序。

堆排序的主要步骤如下:

  1. 构造初始堆:将待排序序列构造成一个大顶堆(或小顶堆)。
  2. 交换数据:将堆顶元素与末尾元素交换,使末尾元素最大(或最小)。然后将剩余元素重新调整为大顶堆。
  3. 重复步骤 2,直到堆中只剩下一个元素未被排序。

二、发展历史

堆排序是一种经典的排序算法,其发展历史可以追溯到 20 世纪初期。以下是堆排序的主要发展历程:

  1. 堆的提出(1902 年):堆最早由德国的计算机科学家卡尔·弗里德里希·高斯在他的一篇论文中提出。高斯提出了一种数据结构,称为 “堆”,用于解决优先队列的问题。然而,他没有提出堆排序算法。
  2. 堆排序算法的提出(1964 年):堆排序算法最早由罗伯特·弗洛伊德(Robert W. Floyd)在其 1964 年的一篇论文中提出。该论文中详细描述了堆排序算法的实现方法和性能分析。
  3. 堆排序的进一步优化(1969 年):在唐纳德·克努斯(Donald Knuth)的《计算机程序设计艺术》(The Art of Computer Programming)第 3 卷中,他对堆排序进行了进一步的优化和详细描述。
  4. 实际应用和性能分析(1970 年代末):随着计算机硬件的发展和堆排序算法的优化,堆排序开始被广泛应用于实际的计算机系统中,并得到了更多的性能分析和改进。
  5. 算法分析和改进(1980 年代至今):随着对算法性能分析和改进方法的深入研究,堆排序算法在实际应用中的效率和性能得到了进一步提升。同时,人们也提出了一些变种的堆排序算法,如改进的堆数据结构和更高效的实现方式,以应对不同场景下的排序需求。

三、处理流程

场景假设:我们需要将下面的无序序列使用堆排序按从小到大进行排序。
workspace (14).png
堆排序的流程如下:

  1. 构造初始堆:我们首先将待排序的序列构造成一个大顶堆。

workspace (8).png

  1. 交换数据:我们将堆顶元素(最大的元素)与末尾元素交换,使末尾元素最大。然后我们将剩余元素重新调整为大顶堆。

workspace (9).png

  1. 重复步骤 2:我们继续将堆顶元素与当前未排序序列的末尾元素交换,并调整剩余元素为大顶堆。重复这个过程,直到堆中只剩下一个元素未被排序。

workspace (13).png

四、算法实现

public class HeapSort {
    // 堆排序函数
    public void sort(int arr[]) {
        int arrayLength = arr.length;

        // 构建初始堆
        for (int i = arrayLength / 2 - 1; i >= 0; i--)
            heapify(arr, arrayLength, i);

        // 一个个从堆中取出元素
        for (int i = arrayLength - 1; i >= 0; i--) {
            // 将当前根节点移到数组末尾
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;

            // 对剩余的堆进行调整
            heapify(arr, i, 0);
        }
    }

    // 堆调整函数
    void heapify(int arr[], int heapSize, int rootNode) {
        int largestNode = rootNode; // 初始化最大节点为根节点
        int leftNode = 2 * rootNode + 1; // 左子节点 = 2*根节点 + 1
        int rightNode = 2 * rootNode + 2; // 右子节点 = 2*根节点 + 2

        // 如果左子节点大于根节点
        if (leftNode < heapSize && arr[leftNode] > arr[largestNode])
            largestNode = leftNode;

        // 如果右子节点大于当前最大节点
        if (rightNode < heapSize && arr[rightNode] > arr[largestNode])
            largestNode = rightNode;

        // 如果最大节点不是根节点
        if (largestNode != rootNode) {
            int swap = arr[rootNode];
            arr[rootNode] = arr[largestNode];
            arr[largestNode] = swap;

            // 递归调整受影响的子树
            heapify(arr, heapSize, largestNode);
        }
    }

    // 打印数组的函数
    static void printArray(int arr[]) {
        int n = arr.length;
        for (int i = 0; i < n; ++i)
            System.out.print(arr[i] + " ");
        System.out.println();
    }

    // 主函数
    public static void main(String args[]) {
        int arr[] = {12, 11, 13, 5, 6, 7};
        int n = arr.length;

        HeapSort hs = new HeapSort();
        hs.sort(arr);

        System.out.println("排序后的数组是");
        printArray(arr);
    }
}

算法时间复杂度分析:

情况时间复杂度计算公式公式解释
最好情况 O ( n l o g n ) O(nlogn) O(nlogn) T ( n ) = n l o g n T(n) = nlogn T(n)=nlogn这是因为无论输入序列的顺序如何,堆排序都需要进行建堆和调整堆的过程,这两个过程的时间复杂度都是 O ( n l o g n ) O(nlogn) O(nlogn)
平均情况 O ( n l o g n ) O(nlogn) O(nlogn) T ( n ) = n l o g n T(n) = nlogn T(n)=nlogn这是因为无论输入序列的顺序如何,堆排序都需要进行建堆和调整堆的过程,这两个过程的时间复杂度都是 O ( n l o g n ) O(nlogn) O(nlogn)
最坏情况 O ( n l o g n ) O(nlogn) O(nlogn) T ( n ) = n l o g n T(n) = nlogn T(n)=nlogn这是因为无论输入序列的顺序如何,堆排序都需要进行建堆和调整堆的过程,这两个过程的时间复杂度都是 O ( n l o g n ) O(nlogn) O(nlogn)

五、算法特性

堆排序(Heap Sort)是一种基于二叉堆数据结构的排序算法。它具有以下特性:

  1. 稳定性: 由于堆排序的过程中会破坏相同元素之间的相对顺序,所以它是一种非稳定的排序算法。因为在堆的构建和调整过程中,相同元素的相对位置可能会改变。
  2. 原地性: 堆排序是一种原地排序算法,它不需要额外的辅助空间,只需要使用输入数组本身进行排序,因此空间复杂度为 O ( 1 ) O(1) O(1)
  3. 不适合小规模数据: 虽然堆排序的时间复杂度较为稳定,但由于它的常数因子较大,因此在数据规模较小的情况下,其性能可能不如插入排序或选择排序等简单排序算法。
  4. 堆的性质: 堆排序利用了堆的性质,即大顶堆(或小顶堆)中每个节点的值都大于(或小于)其子节点的值,这样通过构建堆和调整堆的过程,可以使得整个数组按照升序(或降序)排列。

六、小结

堆排序是一种高效的排序算法,适用于大规模数据的排序任务。它利用了堆这种数据结构的特性,实现了原地排序和稳定的时间复杂度。尽管堆排序的实现过程相对复杂,但其性能优秀,是算法领域中的经典之作。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值