堆排序(Heap:

要讲堆排序之前先要来复习一下完全二叉树的知识。

定义:

对一棵具有n个结点的二叉树按层序编号,如果编号为i(0 <= i <= n)的结点与同样深度的满二叉树编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。

如上面就是一棵完全二叉树。

我们主要会使用的的性质是父结点与子结点的关系:

标号为n的结点的左孩子为2 * n + 1(如果有的话),右孩子为2 * n + 2(如果有的话)

由于完全二叉树的结点的编号是连接的,所以我们可以用一个数组来保存这种数据结构。结点之间的关系可以通过上面的公式进行计算得到。

那什么是堆呢?

堆是具有下列性质的完全二叉树:

每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆(或大根堆);或者每个结点的值都小于或等于其左右孩子结点的值。称为小顶堆(小根堆)。

如图:就是一大根堆。将它转化为数组就是这样的:

{ 9,7,5,6,1,4,2,0,3 }

可以看到一个大概的情况是:0个元素是最大的,前面的元素普遍比后面的大,但这不是绝对的比如例子中的1就跑到4前边去了。

建堆:

那接下来就是第一个问题了,怎么创建一个大根堆呢?也就是解决怎么将给定的一个数组调整成大根堆

假如我们给定一个比较极端的例子{ 10,20,30,40,50,60,70,80 },加个0是为了方便不与结点的编号产生混淆。

对于这样的一个堆,我们应该怎么进行调整呢?

对于堆排序而言,一个比较直观的想法就是从下面开始,把值比较大的元素往上推。这样进行到根位置时,就可以得到一个一个最大的根了

所以,我们应该从最后一个非叶子结点开始调整。

那么怎么确定哪一个是最后一个非叶子结点呢?

其实这完全是可以从完全二叉树的性质中得到的。还记得吗?

左孩子为2 * n + 1

右孩子为2 * n + 2

所以最后一个非叶子结点的编号为array.length / 2 – 1。array就是给定的数组。

所以我们第一个要调整的结点是编号为3的结点,拿它的值跟两个孩子的值做比较(它只有一个孩子)。显然,40和80这两个要交换位置了。

接下来就轮到编号为2的结点了,进行比较后显然是70比较大一点,也进行交换:

同样的道理,编号为1的结点也进行调节:

请注意,这个时候问题就来了。结点1是符合条件了,可以对于以结点3这根的这棵子树就不符合大根堆的要求了,所以我们要重新对编号为3的结点再做一次调整。得到:

我们以同样的方法对编号为0的结点也进行同样的调整。最后就可以得到第一个大根堆了。

这一个过程我们可以称为建堆。我们将数据展开成数组:

{ 80,50,70,40,10,60,30,20 }

不难发现这一个过程中,我们已经把很多值比较大的数字也放到了比较靠前的位置。这一点相当重要,也可以说是堆排序的精华所在。

得到了大根堆之后,我们是可以得到一个最大值了,接下来要做的,就是不断的移除这个堆顶值,与堆尾的值进行交换,堆的长度减小1,然后进行重新的调整

显然,每次都是在堆顶删除,在堆顶开始调整。

之后就是一直重复这个过程直到只剩下一个元素时,就可以完成排序工作了。

相信只要跟着这个思路和这几张图,自己模拟几次还是很好理解的。

接下来看看代码是怎么实现的:

public static void sort(int[] array) {init(array);// 这个过程就是不断的从堆顶移除,调整for (int i = 1; i < array.length; i++) {int temp = array[0];int end = array.length - i;array[0] = array[end];array[end] = temp;adjust(array, 0, end);}}private static void init(int[] array) {for (int i = array.length / 2 - 1; i >= 0; i--) {adjust(array, i, array.length);}}private static void adjust(int[] array, int n, int size) {int temp = array[n]; // 先拿出数据int child = n * 2 + 1; // 这个是左孩子while (child < size) { // 这个保证还有左孩子// 如果右孩子也存在的话,并且右孩子的值比左孩子的大if (child + 1 < size && array[child + 1] > array[child]) {child++;}if (array[child] > temp) {array[n] = array[child];n = child; // n需要重新计算child = n * 2 + 1; // 重新计算左孩子} else {// 这种情况说明左右孩子的值都比父结点的值小break;}}array[n] = temp;}

堆排序的代码量比较多,主要的工作其实是在adjust上。

在adjust这个过程中有几个要注意的:

一个是要注意数组的边界,因为我们每次是把最大值放在最后,然后它就不能再参与调整了。

其次,是最后一个非叶子结点可能只有一个孩子,这也是需要注意的。

堆排序到底快在哪呢?

还是来看一个极端的例子:

{ 1,2,3,4,5,6,7 }

在建堆的时候第一次比较之后的结果应该是这样的:(7和3交换了位置)

{ 1,2,7,4,5,6,3 }

第二次调整之后是:

{ 1,5,7,4,2,6,3 }(5和2交换了位置)

然后是:

{ 7,5,1,4,2,6,3 }(7和1交换了位置,1的位置不对,需要再调整)

{ 7,5,6,4,2,1,3 }(6和1交换了位置)

可以看到,仅仅用了4次比较和4次交换就已经把数组给调整成“比较有序”了。

这个其实是由完全二叉树的性质决定的,因为子结点的编号和父结点的编号存在着两倍(粗略)的差距。

也就说父结点与子结点的数据进行一次交换移动的距离是比较大的(相对于步进)。这个与冒泡和直接插入的“步进”是有明显的区别的。可以说,堆排序的优势在于它具有高效的元素移动效率(这是个人总结,不严谨)

其次,我们在调整堆的时候,可以发现有一半的数据是我们不用动到的。这就使比较次数大大地减少。这个就是很好地利用在建堆的时候保存下来的状态。还是那句话“让上一次的操作结果为下一次操作服务”。

最后回顾一下七个排序:

冒泡排序:好吧,它是中枪次数最多的,最大的优点应该是衬托其他算法的高效。

选择排序:我个人认为它是最符合人的思维习惯的,缺点在于比较次数太多了,但其实它在对少量数据,或者是对于只排序一部分(比如只选出前十名之类的),这种情况下,选择排序就很不错了,因为它可以“部分排序”。

直接插入排序:其实它还不算太差,在应对一些平时的使用时,性能还是可以的。直接插入排序是希尔排序的基础。

希尔排序:这个曾经把我纠结很久的算法,它的外表很难让人看出它的强大。它在几个比较高效的排序算法中代码是最少的,也很容易一次性写出。但理解有点困难。我觉得主要是那个步长序列太难让人一眼看出它到底做了些什么。个人觉得要理解希尔排序首先要弄清楚“基本有序”这个有什么用和希尔排序的前n-1个步长做的就是这些事。先让整个数组变得基本有序,基于一个事实,就是对于基本有序的数组而言,直接插入排序的效率是很高的

归并排序:分治和递归的经典使用,胜就胜在元素的比较次数比较少(貌似说是最少的)。缺点是需要比较大的辅助空间,这个有时会成为限制条件(因为过大的空间消耗有时是不允许的)。

快速排序:如其名,虽存在一定的不稳定性,理论上在最差的情况下,快速排序会退化成选择排序,但可以通过一些手段来使这种情况发生的概率相当的小。

堆排序:个人觉得是最难一口气写出来的排序算法,特别是调整结点的算法每次都要写得小心翼翼(当然,可能是平时写得少)。但它确实是一个很优秀的排序算法,堆排序在元素的移动效率和比较次数上都是比较优秀的。操作系统中堆可是一个重要的数据结构。我记得当时第一次写出堆排序的感叹是“原来数组还可以这么用”。

最后让这几大高手进行一次PK吧,测试的数据是3000000个范围在0 ~ 30000000的随机数。

得到的结果大概是这样的:

    

差距并不算太大,可以看到,最快的还是Java类库提供的方法,它为什么能比快速排序还快呢?

因为它是综合了其他几个算法的特点,比如说在元素很少的时候,直接插入排序可能会快一点,数据量大一点的时候归并可能会快一点,当数据很大的时候,用快速排序可以把数组分成小部分。所以它不是一个人在战斗!

好了,至此,七个排序算法也算是复习了一次,还是那句话,本人菜鸟一个,对这几个算法理解有限,出错之处还请各位指出。

一点个人感受,算法这东西有时以为自己弄懂了,其实还差得远了,有时候看十次书不如自己写一次代码,写了十次代码不如跟别人讲一次。因为这个过程会遇到很多自己以前从没想过的事。这就是我写博客的初衷。

from: https://www.cnblogs.com/yjiyjige/p/3258849.html

七大排序算法的个人总结(三)相关推荐

  1. 排序算法之low B三人组

    排序算法之low B三人组 排序low B三人组 列表排序:将无序列表变成有充列表 应用场景:各种榜单,各种表格,给二分法排序使用,给其他算法使用 输入无序列表,输出有序列表(升序或降序) 排序low ...

  2. 七大排序算法大汇总(上)

    目录 一.[前言]排序的稳定性: 二.七大排序总览 三.插入排序 1.1直接插入排序 1.2直接插入排序优化版--折半插入排序: 2.希尔排序 四.选择排序 1.1选择排序 1.2进阶版选择排序 2. ...

  3. 七大排序算法的个人总结(二)

    归并排序(Merge Sort): 归并排序是一个相当"稳定"的算法对于其它排序算法,比如希尔排序,快速排序和堆排序而言,这些算法有所谓的最好与最坏情况.而归并排序的时间复杂度是固 ...

  4. 七大排序算法的个人总结(一)

    冒泡排序(Bubble Sort): 很多人听到排序第一个想到的应该就是冒泡排序了.也确实,冒泡排序的想法非常的简单:大的东西沉底,汽泡上升.基于这种思想,我们可以获得第一个版本的冒泡: public ...

  5. 排序算法(01)— 三种简单排序(冒泡、插入、选择)

    一.概述 排序是数据处理中十分常见且核心的操作,虽说实际项目开发中很小几率会需要我们手动实现,毕竟每种语言的类库中都有n多种关于排序算法的实现.但是了解这些精妙的思想对我们还是大有裨益的. 1.1 排 ...

  6. 七大排序算法—图文详解(插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序)

    作者:渴望力量的土狗 博客主页:渴望力量的土狗的博客主页 专栏:数据结构与算法 工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器--牛客网 点击免费注册和我一起刷题吧 目录 插入排序: ...

  7. 数据结构七大排序算法图解

    系列文章整合 排序是计算机程序设计中一个非常重要的操作,它将一个数据元素(或记录)的任意序列重新排列成一个按关键字有序的序列,在有序的序列中查找元素的效率很高,但是无序序列只能逐一查找,因此,如何进行 ...

  8. 【排序算法】冒泡排序的三种方法

    冒泡排序算法: 最优时间复杂度为o(n),即当且只当元素本身就是按照从小到大的顺序排列的(这里默认冒泡排序从小到大排列). 最差时间复杂度为o(n^2),当其中有任何一组数据不是按照从小到大排列. # ...

  9. 【好记性不如烂笔头】排序算法之归并排序(三)小和问题

    归并排序(三)小和问题 前言 小和 思考 暴力 引申 思路 代码 注意 前言   上篇博客学习了归并排序,一种是遍历的方式实现,一种是迭代的方式实现,那么归并排序的思路只能用于排序嘛?也不是,这篇博客 ...

最新文章

  1. pip install skimage安装skimage库出错的解决办法
  2. Nancy简单实战之NancyMusicStore(二):打造首页
  3. Java关于Properties用法的总结(一)
  4. Hadoop部署方式-高可用集群部署(High Availability)
  5. 【HDU - 1847】Good Luck in CET-4 Everybody! (巴什博奕,PN图或sg函数)
  6. # Schedulerx正式登陆Ali-k8s应用目录
  7. CF938G Shortest Path Queries
  8. Django中datetime类型的相关操作(记录一下)
  9. linux man 后面的数字,man命令后面的数字
  10. Python 分析国庆热门旅游景点,告诉你哪些地方好玩、便宜、人又少!
  11. 微信小程序时间轴demo_微信小程序 - 时间轴(组件)
  12. 视频教程-思科入门CCNA初级网络工程师视频课程-网络技术
  13. PS快速修改颜色的小方法
  14. K.gather()
  15. H3C路由器多出口NQA+TRACK实现冗余
  16. IT十年人生过客-十二-痛并快乐着
  17. 错误的英语提示翻译 以及经常犯的无错误
  18. get请求 params参数传递以及获取
  19. 企业微信官方认证的好处是什么?
  20. 【排序专训】练习题 士兵站队(中位数应用) 解题报告

热门文章

  1. Java多线程编程模式实战指南(二):Immutable Object模式--转载
  2. iOS pod init 报错
  3. 【风控术语】数字金融欺诈行为名词表
  4. 【采用】【风险管理】(第一篇)风险管理核心指标
  5. 30年货币翻了300倍!如何能跑赢印钞机?
  6. 程序员如果也能像C罗一样自律和勤奋,必将成为大神!
  7. TensorFlow损失函数(loss function) 2017-08-14 11:32 125人阅读 评论(0) 收藏 举报 分类: 深度学习及TensorFlow实现(10) 版权声明:
  8. KNN与K-Means
  9. YEP共享平台释放宜人贷无限潜力
  10. Kaggle 数据挖掘比赛经验分享 (转载)