内部排序:堆排序

前言

堆排序、快速排序、归并排序(下篇会写这两种排序算法)的平均时间复杂度都为O(n*logn)。要弄清楚堆排序,就要先了解下二叉堆这种数据结构。本文不打算完全讲述二叉堆的所有操作,而是着重讲述堆排序中要用到的操作。比如我们建堆的时候可以采用堆的插入操作(将元素插入到适当的位置,使新的序列仍符合堆的定义)将元素一个一个地插入到堆中,但其实我们完全没必要这么做,我们有执行操作更少的方法,后面你会看到,我们基本上只用到了堆的删除操作,更具体地说,应该是删除堆的根节点后,将剩余元素继续调整为堆的操作。先来看二叉堆的定义。

二叉堆

二叉堆其实是一棵有着特殊性质的完全二叉树,这里的特殊性质是指:

1、二叉堆的父节点的值总是大于等于(或小于等于)其左右孩子的值;

2、每个节点的左右子树都是一棵这样的二叉堆。

如果一个二叉堆的父节点的值总是大于其左右孩子的值,那么该二叉堆为最大堆,反之为最小堆。我们在排序时,如果要排序后的顺序为从小到大,则需选择最大堆,反之,选择最小堆,这点通过后面对堆排序分析,你会有所体会。

堆排序

由二叉堆的定义可知,堆顶元素(即二叉堆的根节点)一定为堆中的最大值或最小值,因此如果我们输出堆顶元素后,将剩余的元素再调整为二叉堆,继而再次输出堆顶元素,再将剩余的元素调整为二叉堆,反复执行该过程,这样便可输出一个有序序列,这个过程我们就叫做堆排序。

由于我们的输入是一个无序序列,因此要实现堆排序,我们要先后解决如下两个问题:

1、如何将一个无序序列建成一个二叉堆;

2、在去掉堆顶元素后,如何将剩余的元素调整为一个二叉堆。

针对第一个问题,可能很明显会想到用堆的插入操作,一个一个地插入元素,每次插入后调整元素的位置,使新的序列依然为二叉堆。这种操作一般是自底向上的调整操作,即先将待插入元素放在二叉堆后面,而后逐渐向上将其与父节点比较,进而调整位置。但正如前言中所说,我们完全用不着一个节点一个节点地插入,那我们要怎么做呢?我们需要先来解决第二个问题,解决了第二个问题,第一个问题问题也就迎刃而解了。

调整二叉堆

要分析第二个问题,我们先给出以下前提:

1、我们排序的目标是从小到大,因此我们用最大堆;

2、我们将二叉堆中的元素以层序遍历后的顺序保存在一维数组中,根节点在数组中的位置序号为0。

这样,如果某个节点在数组中的位置序号为i,那么它的左右孩子的位置序号分别为2i+1和2i+2。

为了使调整过程更易于理解,我们采用如下二叉堆来分析(注意下面的分析,我们并没有采用额外的数组来存储每次去掉的堆顶数据):

 

这里数组A中元素的个数为8,很明显最大值为A0,为了实现排序后的元素按照从小到大的顺序排列,我们可以将二叉堆中的最后一个元素A7与A0互换,这样A7中保存的就是数组中的最大值,而此时该二叉树变为了如下情况:

 

时间: 2024-07-30 04:56:19

内部排序:堆排序的相关文章

内部排序算法的总结

内部排序总结 这篇博文我们简要地总结下各种内部排序方法. 这10种排序算法中,前面7种属于建立在"比较"基础上的排序算法,通过决策树已经证明,任何基于比较进行的排序算法的时 间复杂度不可能再优于O(n*logn).后面3种不是建立在比较的基础上的,因此,可以达到线性运行时间. 下面我们给出各种排序方法的时空复杂度的表格(属于自己总结,有不对的地方,希望大家指正或补充). 返回栏目页:http://www.bianceng.cnhttp://www.bianceng.cn/Program

数据结构内部排序实验报告

问题描述 数据结构内部排序实验报告 一.实验目的 1.掌握排序的有关概念和特点. 2.熟练掌握直接插入排序.希尔排序.冒泡排序.快速排序.简单选择排序.堆排序.归并排序.基数排序等算法的基本思想.. 3.关键字序列有序与无序,对于不同的排序方法有不同的影响,通过该实验进一步加深理解. 二.实验内容 设有关键字序列k={ 12 , 45 , 21 , 12 , 30 , 2 , 68 , 33 },试用各种排序算法进行排序. 三.实验环境 TC或VC++ 四.实验步骤 1.从键盘输入上述8个整数,

基于C++实现的各种内部排序算法汇总_C 语言

提起排序算法相信大家都不陌生,或许很多人已经把它们记得滚瓜烂熟,甚至随时可以写出来.是的,这些都是最基本的算法.这里就把各种内部排序算法总结归纳了一下,包括插入排序(直接插入排序,折半插入排序,希尔排序).交换排序(冒泡排序,快速排序).选择排序(简单选择排序,堆排序).2-路归并排序.(另:至于堆排序算法,前面已经有一篇文章针对堆排序的算法实现做了详细的描述) C++实现代码如下: /*******************************************************

内部排序:计数排序、基数排序和桶排序

前言 最后三种排序算法了,由于都不是基于比较的排序,因此这三种排序算法可以以线性时间运行.但是因为限制条件的特殊性,因此应用面没有基于元素比较的排序算法广,但是在很多特定的情况下还是蛮有用途的,而且效率极高. 计数排序 计数排序是建立在这样的前提条件下的:假设n个输入元素的每一个都是0到k区间内的一个整数,其中k为某个整数.因此我们后面所写的程序也只是针对0到k之间的元素进行排序,换句话说,排序元素中不能有负数. 计数排序的基本思想是:对一个输入元素x,先确定所有输入元素中小于x的元素个数,那么

内部排序:插入排序和希尔排序的N种实现

前言 本来想将所有的内部排序总结为一篇博文,但是随着研究的深入,还是放弃了这个念头,斟前酌后,还是觉得分开来写比较好,具体原因,看完本篇博文也就自然明了了. 本篇文章主要探讨插入排序和希尔排序,之所将二者放在一起,很明显,是因为希尔排序是建立在插入排序的基础之上的. 注:以下各排序算法的N种实现方法大部分都是我根据算法思想,自己写出来的,或者是参考其本身的经典实现,我自己都已测试通过,但不敢保证一定都没问题,如果有疑问,欢迎指出. 插入排序 插入排序的思想很简单,它的基本操作就是将一个数据插入到

常用内部排序算法之五:希尔排序

前言 在前面介绍常用内部排序算法的文章中,我们知道在简单排序算法中,直接插入排序算法的时间复杂度是要优于冒泡排序和简单选择排序的.而这里要讲的希尔排序则是优于直接插入排序的.而且希尔排序打破了原来简单排序中时间复杂度不能超过O(n2)的神话.这也是希尔排序伟大的地方,而且希尔排序不同于之前三种简单排序,希尔排序是以一个科学家的名字进行命名的. 希尔排序的原理 由于希尔排序是在直接插入排序的基础上进行改进的,所以为了更好理解希尔排序,需要再次将直接插入排序请出来.我们知道直接插入排序的基本思想是将

常用内部排序算法之二:快速排序

前言 快速排序可以说是内部排序算法中的高手,之所以称为快速排序,是因为快速排序算法从整体性能上讲是排序冠军.快速排序算法的思想是:通过一趟快速排序将待排序的记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的记录的关键字小,则可分别对这两部分记录继续进行排序,达到整个记录有序.实现快速排序算法的核心是partition函数,这个函数的主要目的先选取当中的一个关键字(称为枢轴),然后尽可能将他放在一个位置,使得它左边的值都比它小,右边的值比它大. 快速排序算法实现 package com.

内部排序——希尔插入排序

直接插入排序在时间复杂度上优势不明显.达到O(n2)的水平了,所以需要想办法降低时间复杂度是很有必要的.当记录的排序就是所求的排序时,时间复杂度会大幅下降,为O(n).这是最理想的状态,当顺序刚好是逆序的时候,时间复杂度最大为O(n2).所以记录越是有序,时间复杂度越低.这个和快速排序不同,大家都知道快速排序在有序的情况下效果是很差的吧. 现在的问题是,如何使得记录变得有序,这个也是我们求的最后结果.希尔排序是一种很好的选择,它的原理是使得记录大体上有序,虽然不是所有都有序,但是大体上有序也是很

内部排序算法:堆排序

基本思想 堆的定义 n个关键字序列kl,k2,-,kn称为堆,当且仅当该序列满足如下性质之一(简称堆性质): ki≤k2i且ki≤k2i+1 或 ki≥k2i且ki≥k2i+1(1≤i≤FLOOR(n/2)) 若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若 存在)结点的关键字. 小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小的. 大根堆:根结点(亦称为堆顶)的关键