堆排序(Heap Sort)

简介: 算法介绍算法描述图片演示动图演示算法分析代码实现参考

算法介绍


     顾名思义,堆排序就是利用堆(Heap)这种数据结构进行排序,在堆排序算法中,我们使用的是最大堆(大根堆),堆排序是一种选择排序。


算法描述


 第一步:利用build_max_heap函数将输入数据构建大根堆。


 第二步:因为大根堆中的最大元素总是在根节点,通过把它与堆的最后一个元素交换,就可以把该元素放在正确的位置(从此可以看出堆排序是一种选择排序)。


 第三步:从堆中去掉最后一个元素,剩余结点中,原来根的孩子结点仍然是大根堆,而新的根结点可能会违背最大堆性质,因此,我们需要调用max_heapify函数维护最大堆性质。


 第四步:重复上述过程,直到堆的大小减小到1;


图片演示





动图演示


20210515155046136.gif


算法分析


 时间复杂度:O(NlogN)


 空间复杂度:O(1)


 稳定性:不稳定


 堆排最坏时间复杂度、空间复杂度都优于快速排序,为什么实践中更多使用快排而不是堆排呢?


 一、快速排序的过程中,访问数据是顺序进行访问的,而且有非常精练和高度优化的内部循环,但是在堆排序中,需要维护堆性质,导致需要跳着数组下标去进行访问元素,对CPU缓存不友好。


 二、堆排序过程使用更多的比较次数,值得一提的是,对于不同的语言,比较的代价是不同的,具体可参考归并排序的分析。


代码实现

// C++
// 堆排序
void heap_sort(int a[], int length) {
    build_max_heap(a, length);// 建大根堆
    // 每次将最大元素放在正确位置
    for (int i = length - 1; i > 0; --i) {
        int tmp = a[i];// 交换堆根与堆最后一个元素
        a[i] = a[0];
        a[0] = tmp;
        max_heapify(a, i, 0);// 维护大根堆性质
    }
}
// 维护大根堆性质
void max_heapify(int a[], int length, int i) {
    int left = 2 * i + 1;// 左孩子位置
    int right = left + 1;// 右孩子位置
    int largeindex = i;// 记录当前结点与左孩子右孩子中最大
    // 判断左孩子是否大于当前结点
    if (left < length && a[left] > a[largeindex]) {
        largeindex = left;
    }
    // 判断右孩子是否大于当前最大结点
    if (right < length && a[right] > a[largeindex]) {
        largeindex = right;
    }
    // 判断是否已经不需维护
    if (largeindex != i) {
        int tmp = a[i];// 交换
        a[i] = a[largeindex];
        a[largeindex] = tmp;
        // 由于以该结点为根的子树有可能违反大根堆性质
        // 因此递归调用max_heapify
        max_heapify(a, length, largeindex);
    }
}
// 建堆
void build_max_heap(int a[], int length) {
    // 对length / 2到0维护大根堆性质
    for (int i = length / 2; i >= 0; --i) {
        max_heapify(a, length, i);
    }
}


参考


算法导论

数据结构与算法分析(Java语言描述)

数据结构(C语言版)

————————————————

版权声明:本文为CSDN博主「Acx7」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/Acx77/article/details/116852890

相关文章
|
1月前
|
人工智能 搜索推荐 算法
sort-04-heap sort 堆排序算法详解
这是一个关于排序算法的系列文章摘要,包括了10篇关于不同排序算法的链接,如冒泡排序、快速排序、选择排序、堆排序等。堆排序是一种基于比较的排序算法,利用了近似完全二叉树的结构并满足最大堆或最小堆的性质。最大堆中,每个节点的值都大于或等于其子节点。文章详细解释了最大堆的概念、节点访问方式以及堆的操作,包括堆调整和堆排序的过程,并通过图解展示了堆排序的具体步骤。此外,还提供了一个Java实现的堆排序代码示例。整个排序系列来源于一个开源项目,读者可以通过链接查看完整内容。
sort-04-heap sort 堆排序算法详解
|
1月前
|
搜索推荐 算法 Java
sort-05-insert sort 插入排序算法详解
这是一个关于排序算法的系列文章总结,包括冒泡排序、快速排序、选择排序、堆排序、插入排序等10种排序算法的详细讲解和Java实现。插入排序是一种简单直观的排序算法,通过构建有序序列并逐个插入新元素来排序。提供的Java代码展示了插入排序的实现过程,并附有测试示例。所有算法已开源在GitHub项目[https://github.com/houbb/sort](https://github.com/houbb/sort)中。
|
1月前
|
搜索推荐 算法 Java
sort-08-counting sort 计数排序
这是一个关于排序算法的系列文章摘要。文章详细介绍了多种排序算法,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及大文件外部排序。计数排序是一种线性时间复杂度的稳定排序算法,由 Harold H. Seward 在1954年提出。基础版计数排序通过创建一个与最大元素等长的新数组来统计每个元素出现的次数,然后填充排序结果。改良版则考虑了空间浪费问题,通过找出最小值来减少数组长度。此外,还提出了使用 TreeMap 来扩展排序算法以适应非数字元素的情况。
|
1月前
|
存储 搜索推荐 算法
sort-09-bucket sort 桶排序
这是一个关于排序算法的系列文章总结,包括冒泡、快速、选择、堆、插入、希尔、归并、计数、桶和大文件外部排序。桶排序是一种将元素分配到有限数量的桶中,然后对每个桶分别排序的算法。在元素均匀分布的情况下,桶排序的时间复杂度为线性`O(n)`。文章还提供了一个Java实现示例及复杂度分析。完整代码可在GitHub的开源项目中找到。
|
1月前
|
搜索推荐 算法 Java
sort-07-merge sort 归并排序
这是一个关于排序算法的系列文章摘要。文章涵盖了多种排序算法的详细解释,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及大文件外部排序。归并排序是一种效率为O(nlogn)的排序算法,基于分治法,将序列分成两半,分别排序后再合并。文章提供了Java实现的递归和迭代版本。在归并排序的递归实现中,代码通过不断拆分和合并子序列完成排序,而迭代实现则是通过逐步增大子序列长度并进行两两归并来排序。整个系列可在GitHub找到相关源码。
|
1月前
|
搜索推荐 算法 Java
sort-02-QuickSort 快速排序到底快在哪里?
这是一个关于排序算法的系列文章的摘要。文章详细介绍了各种排序算法,包括冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序、计数排序、桶排序以及处理大文件的外部排序。特别强调了快速排序,它是1959年由Tony Hoare发明的,平均时间复杂度为O(n log n),优于其他如合并排序和堆排序。快速排序采用分而治之的策略,选取基准元素,通过分区操作将数组分成两部分,然后递归地对这两部分进行排序。文章还通过示例和Java代码解释了快速排序的步骤和实现。最后,提供了开源项目的链接,供读者进一步学习和研究。
|
1月前
|
搜索推荐 Python
堆排序(Heap Sort)
堆排序(Heap Sort)是一种选择排序算法,通过将待排序的元素构建成一个最大堆(或最小堆),然后将堆顶元素与堆尾元素交换,将堆的大小减一,再调整堆,使其重新满足最大堆(或最小堆)的性质。重复以上步骤,直到堆中只剩下一个元素,即排序完成。
29 2
|
8月前
|
存储 搜索推荐 算法
计数排序(Counting Sort)详解
计数排序(Counting Sort)是一种非比较排序算法,其核心思想是通过计数每个元素的出现次数来进行排序,适用于整数或有限范围内的非负整数排序。这个算法的特点是速度快且稳定,适用于某些特定场景。在本文中,我们将深入探讨计数排序的原理、步骤以及性能分析。
190 1
计数排序(Counting Sort)详解
|
11月前
|
算法 搜索推荐 Java
|
12月前
|
搜索推荐
十大排序之Heap Sort 堆排序
十大排序之Heap Sort 堆排序