堆排序是一种树形选择排序,是对直接选择排序的有效改进。
堆的构建--》堆排:

-<使/,>-/--------


C++




1、算法思想

堆:具有n个元素的序列(k1,k2,...,kn),当且仅当满足

时称之为堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最小项(最大项)。
若以一维数组存储一个堆,则堆对应一棵完全二叉树,且所有非叶结点的值均不大于(或不小于)其子女的值,根结点(堆顶元素)的值是最小(或最大)的。如:

(a)大顶堆序列:(96, 83,27,38,11,09)

(b)  小顶堆序列:(12,36,24,85,47,30,53,91)

初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序。

因此,实现堆排序需解决两个问题:
1. 如何将n 个待排序的数建成堆;
2. 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆。

首先讨论第二个问题:输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程。
调整大顶堆的方法:

1)设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交换),堆被破坏,其原因仅是根结点不满足堆的性质。

2)将根结点与左、右子树中较大元素的进行交换。

3)若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 (2).

4)若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 (2).

5)继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。

称这个自 根结点 到 叶子结点 的调整过程为筛选。如图:

再讨论对n 个元素初始建堆的过程。
建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程。

1)n 个结点的完全二叉树,则最后一个结点是第个结点的子树。【向下取整】

2)筛选从第个结点为根的子树开始,该子树成为堆。

3)之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。

如图建堆初始过程:无序序列:(49,38,65,97,76,13,27,49)

2、代码实现
package sort;public class HeapSort {public static void main(String[] args) {int[] src = { 66, 89, 8, 123, 9, 44, 55, 37, 200, 127, 98 };System.out.println("初始值:");print(src);HeapSort(src, src.length);System.out.println("堆排后:");print(src);}/*** 大顶堆排序算法*/private static void HeapSort(int src[], int length) {// 大顶堆排序算法// 初始化堆,从最后一个节点 i= (length-1) / 2for (int i = (length - 1) >> 1; i >= 0; --i)HeapAdjust(src, i, length);for (int i = length - 1; i > 0; --i) {// 从堆尾往前调整src[i] = src[0] + (src[0] = src[i]) * 0;// 弹出堆顶最大值,堆尾值填补HeapAdjust(src, 0, i);// 重新调整堆}}/*** src[i+1,…. ,j]已经成堆,调整 i 的子树为堆.* * @param src是待调整的堆数组* @param i是待调整的数组元素的位置* @param j是待调整数组的长度*/private static void HeapAdjust(int src[], int i, int j) {// 对下标为i的节点,调整其子树为堆int temp = src[i];int child = 2 * i + 1; // 左孩子的位置。(child+1 为当前调整结点的右孩子的位置)while (child < j) {// 防止第一次length为偶数12时,i=5与child=11非父子关系if (child + 1 < j && src[child] < src[child + 1]) { // 定位较大的的孩子结点++child;}if (src[i] < src[child]) { // 如果较大的子结点大于父结点src[i] = src[child]; // 那么把较大的子结点往上移动,替换它的父结点i = child; // 更新i,以便使新子树为堆(子树结构可能改变)src[i] = temp; // 父结点放到比它大的孩子结点上(temp值未变)child = 2 * i + 1;// 若child<length,则继续循环建堆} else { // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出break;// 防止死循环}}print(src);}private static void print(int a[]) {for (int i : a) {System.out.print(i + " ");}System.out.println();}
}
3、算法分析
        堆排序也是一种不稳定的排序算法。 

堆排序优于简单选择排序的原因:

直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。

  堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。因为其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上。

堆排序在最坏的情况下,其时间复杂度也为O(nlogn)。相对于快速排序来说,这是堆排序的最大优点。此外,堆排序仅需一个记录大小的供交换用的辅助存储空间。

        建堆时对于每个非终端结点来说,其实最多进行两次比较和互换操作,比较次数不超过4n 次,因此整个构建堆的时间复杂度为O(n)。
        设树深度为k,从根到叶的筛选,元素比较次数至多2(k-1)次,交换记录至多k 次。所以,在建好堆后,排序过程中的筛选次数不超过下式:
    因此堆排序最坏情况下,时间复杂度也为:O(nlogn )
        
4、算法改进

堆排序可通过树形结构保存部分比较结果,可减少比较次数。

源码地址:https://github.com/zxiaofan/Algorithm/blob/master/src/sort/HeapSort.java
欢迎个人转载,但须在文章页面明显位置给出原文连接;
未经作者同意必须保留此段声明、不得随意修改原文、不得用于商业用途,否则保留追究法律责任的权利。
【 CSDN 】:csdn.zxiaofan.com
【GitHub】:github.zxiaofan.com
如有任何问题,欢迎留言。祝君好运!
Life is all about choices!
将来的你一定会感激现在拼命的自己!

堆排HeapSort相关推荐

  1. 【LeetCode笔记】215. 数组中的第K个最大元素(Java、快排、堆排、并发快排)

    文章目录 题目描述 思路 & 代码 快排 基于 Fork / Join 的并发快排 针对 topK 的快排优化 堆排 基本堆排 结合题目的堆排 二刷 题目描述 大名鼎鼎的TOP K,主要考察排 ...

  2. 【软件】[Qt\C++] 冒泡、希尔、堆排、基数、快排 5种排序Gui界面带对比——使用Qt实现

    完整代码 https://github.com/gongfpp/sortsWithQt 成品(随机生成数字后全过了一遍) 一. 实验任务(实验题目.目的) 实现5种排序(冒泡.希尔.堆排.基数.快排) ...

  3. 【C】二叉树--顺序结构(详解堆的实现,topK排序、堆排)、和链式结构(链式结构的遍历、链式结构常见递归操作以及练习题)

    本章我们将引入树的概念并详细介绍二叉树.我们会在介绍顺序二叉树基础上,进一步介绍堆以及堆的实现,并以此为依据详解topK排序.堆排等问题:然后我们会介绍链式二叉树的实现以及各种操作.最后,我们也会给出 ...

  4. Java面试宝典系列之基础面试题-常见的几种排序算法-插入、选择、冒泡、快排、堆排等

    常见的几种排序算法-插入.选择.冒泡.快排.堆排: https://blog.csdn.net/zhangerqing/article/details/8831542

  5. Python 实现快排、堆排

    Python 实现快排.堆排 1.快排 原理:(升序) 选取数组的首个元素做为中间值,缓存这个中间值,该位置变为空: 从右到左和中间值对比,找到第一个小于中间值的元素,把该值放到左边的空位,该位置变为 ...

  6. 数据结构与算法(C++)– 堆排(Heap Sort)

    堆排(Heap Sort) 1.概念 完全二叉树特点: 对于完全二叉树中任一点 i: 左孩子的位置为: 2i 右孩子的位置为:2i+1 父节点位置为:i/2 向下取整 最小二叉堆:根节点的值小于子树的 ...

  7. 旋转数组的最小数字+堆排介绍19.9.14

    题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个 ...

  8. 排序算法 | 快排、冒泡、堆排、归并、基数、递归、希尔、计数

    文章目录 写在前面 排序 1. 基数排序`稳定` 2. 归并排序`稳定`merge sort 3. 快速排序`不稳定`quick sort 4. 堆排序`不稳定`heap sort 大根堆 小根堆 5 ...

  9. Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等

    本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排 ...

最新文章

  1. 一点一滴培养你的领导气质
  2. Android中的任务栈
  3. JavaScript 函数定义方式
  4. j2ee servlet 和 threadlocal ,synchronized 与 web容器
  5. Oracle的启动和关闭
  6. php显示网络图片,PHP在服务器端怎么显示图片呢?
  7. Hadoop 架构已凋谢 ?!
  8. Spring常用注解详解大全(建议收藏)
  9. PHP生成二维码与识别二维码,jq生成二维码
  10. EasyUI 中combobox利用拼音进行检索
  11. Unity导入图片为何会失真
  12. android studio白屏,首次集成到Android studio的工程运行起来就是白屏
  13. android 7.0下载地址,android 7.0 Downloadprovider 下载流程
  14. Redis入门完整教程:复制原理
  15. opencv生成棋盘格
  16. 经验模式分解(EMD)——简介及Matlab工具箱安装
  17. Vue 项目飞鸟头条后台管理系统
  18. 2022年团体程序设计天梯赛C++个人题解附带解题思路
  19. 2t移动硬盘linux无法格式化,移动硬盘无法格式化怎么办实际解决方法
  20. Web3.0 兴起,元宇宙或接管互联网?

热门文章

  1. iOS 相机 点击拍照,长按录像
  2. PS无法直接拖入图片如何解决?
  3. 代号“猫”:微软云端的特种部队 | 专题
  4. 带你玩转指针——指针进阶(二)
  5. 如何实现店铺稳定盈利?做店铺需要知道的几点技巧
  6. Error creating bean with name ,Unsatisfied dependency expressed through field
  7. RS-SLAM A Robust Semantic SLAM in Dynamic Environments Based on RGB-D Sensor
  8. Flutter学习总结(四)生命周期
  9. 双下方法(魔法方法)
  10. 下载Windows Terminal送Cascadia Code字体