【初探】 堆排序
2017-09-10 15:54
155 查看
堆排序介绍
● 堆排序(Heap Sort): 是指利用堆这种数据结构所设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn) (堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法),它也是不稳定排序。
如果一个二叉堆的父节点的值总是大于其左右孩子的值,那么该二叉堆为最大堆,反之为最小堆。我们在排序时,如果要排序后的顺序为从小到大,则需选择最大堆,反之,选择最小堆。
堆的概念
在程序设计领域,堆(Heap)的概念主要有以下两种:
数据结构中的堆实质上是满足一定性质的完全二叉树:二叉树中任一非叶子结点关键字的值均小于(大于)它的孩子结点的关键字。
● 堆是一棵顺序存储的完全二叉树,完全二叉树适合采用顺序存储的方式,因此一个数组可以看成一个完全二叉树。
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
如上图所示,序列R{3, 8, 15, 31, 25}是一个典型的小根堆。
堆中有两个父结点,元素3和元素8。
可以看出,它们满足以下规律:
设当前元素在数组中以R[i]表示,那么,
堆排序
● 由二叉堆的定义可知,堆顶元素(即二叉堆的根节点)一定为堆中的最大值或最小值,因此如果我们输出堆顶元素后,将剩余的元素再调整为二叉堆,继而再次输出堆顶元素,再将剩余的元素调整为二叉堆,反复执行该过程,这样便可输出一个有序序列, 这个过程我们就叫做堆排序。
由于我们的输入是一个无序序列,因此要实现堆排序,我们要先后解决如下两个问题:
针对第一个问题,可能很明显会想到用堆的插入操作,一个一个地插入元素,每次插入后调整元素的位置,使新的序列依然为二叉堆。这种操作一般是自底向上的调整操作, 即先将待插入元素放在二叉堆后面,而后逐渐向上将其与父节点比较,进而调整位置。
我们完全用不着一个节点一个节点地插入,那我们要怎么做呢?我们需要先来解决第二个问题,解决了第二个问题,第一个问题问题也就迎刃而解了。
调整二叉堆
要分析第二个问题,我们先给出以下前提:
为了使调整过程更易于理解,我们采用如下二叉堆来分析(注意下面的分析,我们并没有采用额外的数组来存储每次去掉的堆顶数据):
这里数组A中元素的个数为8,很明显最大值为A0,为了实现排序后的元素按照从小到大的顺序排列,我们可以将二叉堆中的最后一个元素A7与A0互换,这样A7中保存的就是数组中的最大值,而此时该二叉树变为了如下情况:
为了将其调整为二叉堆,我们需要寻找4应该插入的位置。为此,我们让4与它的孩子节点中最大的那个,也就是其左孩子7,进行比较,由于4<7,我们便把二者互换,这样二叉树便变成了如下的形式:
接下来,继续让4与其左右孩子中的最大者,也就是6,进行比较,同样由于4<6,需要将二者互换,这样二叉树变成了如下的形式:
这样便又构成了二叉堆,这时候A0为7,是所有元素中的最大元素。同样我们此时继续将二叉堆中的最后一个元素A6和A0互换,这样A6中保存的就是第二大的数值7,而A0就变为了3,形式如下:
为了将其调整为二叉堆,一样将3与其孩子结点中的最大值比较,由于3<6,需要将二者互换,而后继续和其孩子节点比较,需要将3和4互换,最终再次调整好的二叉堆形式如下:
一样将A0与此时堆中的最后一个元素A5互换,这样A5中保存的便是第三大的数值,再次调整剩余的节点,如此反复,直到最后堆中仅剩一个元素,这时整个数组便已经按照从小到大的顺序排列好了。
堆排序基本思想及步骤
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
以上思想可归纳为两个操作:
下面通过几张示例图,帮助理解!
步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
a.假设给定无序序列结构如下
此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。
找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。
这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。
此时,我们就将一个无需序列构造成了一个大顶堆。
步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
将堆顶元素9和末尾元素4进行交换
.重新调整结构,使其继续满足堆定义
再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.
后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
下面演示heap_sort (a, n)对a={20,30,90,40,70,110,60,10,100,50,80}, n=11进行堆排序过程。下面是数组a对应的初始化结构:
1 初始化堆
在堆排序算法中,首先要将待排序的数组转化成二叉堆。
下面演示将数组{20,30,90,40,70,110,60,10,100,50,80}转换为最大堆{110,100,90,40,80,20,60,10,30,50,70}的步骤。
1.1 i=11/2-1,即i=4
上面是maxheap_down(a, 4, 9)调整过程。maxheap_down(a, 4, 9)的作用是将a[4…9]进行下调;a[4]的左孩子是a[9],右孩子是a[10]。调整时,选择左右孩子中较大的一个(即a[10])和a[4]交换。
1.2 i=3
上面是maxheap_down(a, 3, 9)调整过程。maxheap_down(a, 3, 9)的作用是将a[3…9]进行下调;a[3]的左孩子是a[7],右孩子是a[8]。调整时,选择左右孩子中较大的一个(即a[8])和a[4]交换。
1.3 i=2
上面是maxheap_down(a, 2, 9)调整过程。maxheap_down(a, 2, 9)的作用是将a[2…9]进行下调;a[2]的左孩子是a[5],右孩子是a[6]。调整时,选择左右孩子中较大的一个(即a[5])和a[2]交换。
1.4 i=1
上面是maxheap_down(a, 1, 9)调整过程。maxheap_down(a, 1, 9)的作用是将a[1…9]进行下调;a[1]的左孩子是a[3],右孩子是a[4]。调整时,选择左右孩子中较大的一个(即a[3])和a[1]交换。交换之后,a[3]为30,它比它的右孩子a[8]要大,接着,再将它们交换。
1.5 i=0
上面是maxheap_down(a, 0, 9)调整过程。maxheap_down(a, 0, 9)的作用是将a[0…9]进行下调;a[0]的左孩子是a[1],右孩子是a[2]。调整时,选择左右孩子中较大的一个(即a[2])和a[0]交换。交换之后,a[2]为20,它比它的左右孩子要大,选择较大的孩子(即左孩子)和a[2]交换。
调整完毕,就得到了最大堆。此时,数组{20,30,90,40,70,110,60,10,100,50,80}也就变成了{110,100,90,40,80,20,60,10,30,50,70}。
第2部分 交换数据
在将数组转换成最大堆之后,接着要进行交换数据,从而使数组成为一个真正的有序数组。
交换数据部分相对比较简单,下面仅仅给出将最大值放在数组末尾的示意图。
上面是当n=10时,交换数据的示意图。
当n=10时,首先交换a[0]和a[10],使得a[10]是a[0…10]之间的最大值;然后,调整a[0…9]使它称为最大堆。交换之后:a[10]是有序的!
当n=9时, 首先交换a[0]和a[9],使得a[9]是a[0…9]之间的最大值;然后,调整a[0…8]使它称为最大堆。交换之后:a[9…10]是有序的!
…
依此类推,直到a[0…10]是有序的。
最后再看一个示例:
设有一个无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 }。
初始堆:
构造了初始堆后,我们来看一下完整的堆排序处理:
还是针对前面提到的无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 } 来加以说明。
相信,通过以上两幅图,应该能很直观的演示堆排序的操作处理。
算法分析
堆排序算法的总体情况
假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),需要遍历多少次呢?
堆排序是采用的二叉堆进行排序的,二叉堆就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的定义,它的深度至少是lg(N+1)。最多是多少呢?由于二叉堆是完全二叉树,因此,它的深度最多也不会超过lg(2N)。因此,遍历一趟的时间复杂度是O(N),而遍历次数介于lg(N+1)和lg(2N)之间;因此得出它的时间复杂度是O(N*lgN)。
堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为nlogn。所以堆排序时间复杂度一般认为就是O(nlogn)级。
我们在每次重新调整堆时,都要将父节点与孩子节点比较,这样,每次重新调整堆的时间复杂度变为O(logn),而堆排序时有n-1次重新调整堆的操作,建堆时有((len-1)/2+1)次重新调整堆的操作,因此堆排序的平均时间复杂度为O(n*logn)。由于我们这里没有借用辅助存储空间,因此空间复杂度为O(1)。
堆排序在排序元素较少时有点大才小用,待排序列元素较多时,堆排序还是很有效的。因为其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上,
另外,堆排序在最坏情况下,时间复杂度也为O(n*logn)。相对于快速排序(平均时间复杂度为O(n*logn),最坏情况下为O(n*n)),这是堆排序的最大优点。
算法稳定性
● 堆排序是不稳定的算法,它不满足稳定算法的定义。它在交换数据的时候,是比较父结点和子节点之间的数据,所以,即便是存在两个数值相等的兄弟节点,它们的相对顺序在排序也可能发生变化。
算法稳定性的定义 – 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
● 堆排序(Heap Sort): 是指利用堆这种数据结构所设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn) (堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法),它也是不稳定排序。
注意: 在学习堆排序之前,有必要了解堆!若读者不熟悉堆, 建议先了解堆(建议可以通过二叉堆,左倾堆,斜堆,二项堆或斐波那契堆等文章进行了解), 然后再来学习本章。
二叉堆
二叉堆其实是一棵有着特殊性质的完全二叉树,这里的特殊性质是指:1、二叉堆的父节点的值总是大于等于(或小于等于)其左右孩子的值; 2、每个节点的左右子树都是一棵这样的二叉堆。
如果一个二叉堆的父节点的值总是大于其左右孩子的值,那么该二叉堆为最大堆,反之为最小堆。我们在排序时,如果要排序后的顺序为从小到大,则需选择最大堆,反之,选择最小堆。
堆的概念
在程序设计领域,堆(Heap)的概念主要有以下两种:
(1)一种数据结构,逻辑上是一颗完全二叉树,存储上是一个数组对象(二叉堆)。也正是本文谈及的堆。 (2)存储区,是软件系统可以编程的内存区域(即就是动态申请的区域)。
数据结构中的堆实质上是满足一定性质的完全二叉树:二叉树中任一非叶子结点关键字的值均小于(大于)它的孩子结点的关键字。
在小根堆中,第一个元素(完全二叉树的根结点)的关键字最小; 大根堆中,第一个元素(完全二叉树的根结点)的关键字最大, 显然,堆中任一子树仍是一个堆。
● 堆是一棵顺序存储的完全二叉树,完全二叉树适合采用顺序存储的方式,因此一个数组可以看成一个完全二叉树。
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子
该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
如上图所示,序列R{3, 8, 15, 31, 25}是一个典型的小根堆。
堆中有两个父结点,元素3和元素8。
元素3在数组中以R[0]表示,它的左孩子结点是R[1],右孩子结点是R[2]。 元素8在数组中以R[1]表示,它的左孩子结点是R[3],右孩子结点是R[4],它的父结点是R[0]。
可以看出,它们满足以下规律:
设当前元素在数组中以R[i]表示,那么,
(1) 它的左孩子结点是:R[2*i+1]; //大顶堆也是如此 (2) 它的右孩子结点是:R[2*i+2]; //大顶堆也是如此 (3) 它的父结点是:R[(i-1)/2]; //大顶堆也是如此 (4) R[i] <= R[2*i+1] 且 R[i] <= R[2*i+2]。 // 小顶堆的定义
堆排序
● 由二叉堆的定义可知,堆顶元素(即二叉堆的根节点)一定为堆中的最大值或最小值,因此如果我们输出堆顶元素后,将剩余的元素再调整为二叉堆,继而再次输出堆顶元素,再将剩余的元素调整为二叉堆,反复执行该过程,这样便可输出一个有序序列, 这个过程我们就叫做堆排序。
由于我们的输入是一个无序序列,因此要实现堆排序,我们要先后解决如下两个问题:
1、如何将一个无序序列建成一个二叉堆; 2、在去掉堆顶元素后,如何将剩余的元素调整为一个二叉堆。
针对第一个问题,可能很明显会想到用堆的插入操作,一个一个地插入元素,每次插入后调整元素的位置,使新的序列依然为二叉堆。这种操作一般是自底向上的调整操作, 即先将待插入元素放在二叉堆后面,而后逐渐向上将其与父节点比较,进而调整位置。
我们完全用不着一个节点一个节点地插入,那我们要怎么做呢?我们需要先来解决第二个问题,解决了第二个问题,第一个问题问题也就迎刃而解了。
调整二叉堆
要分析第二个问题,我们先给出以下前提:
1、我们排序的目标是从小到大,因此我们用最大堆; 2、我们将二叉堆中的元素以层序遍历后的顺序保存在一维数组中,根节点在数组中的位置序号为0。
为了使调整过程更易于理解,我们采用如下二叉堆来分析(注意下面的分析,我们并没有采用额外的数组来存储每次去掉的堆顶数据):
这里数组A中元素的个数为8,很明显最大值为A0,为了实现排序后的元素按照从小到大的顺序排列,我们可以将二叉堆中的最后一个元素A7与A0互换,这样A7中保存的就是数组中的最大值,而此时该二叉树变为了如下情况:
为了将其调整为二叉堆,我们需要寻找4应该插入的位置。为此,我们让4与它的孩子节点中最大的那个,也就是其左孩子7,进行比较,由于4<7,我们便把二者互换,这样二叉树便变成了如下的形式:
接下来,继续让4与其左右孩子中的最大者,也就是6,进行比较,同样由于4<6,需要将二者互换,这样二叉树变成了如下的形式:
这样便又构成了二叉堆,这时候A0为7,是所有元素中的最大元素。同样我们此时继续将二叉堆中的最后一个元素A6和A0互换,这样A6中保存的就是第二大的数值7,而A0就变为了3,形式如下:
为了将其调整为二叉堆,一样将3与其孩子结点中的最大值比较,由于3<6,需要将二者互换,而后继续和其孩子节点比较,需要将3和4互换,最终再次调整好的二叉堆形式如下:
一样将A0与此时堆中的最后一个元素A5互换,这样A5中保存的便是第三大的数值,再次调整剩余的节点,如此反复,直到最后堆中仅剩一个元素,这时整个数组便已经按照从小到大的顺序排列好了。
堆排序基本思想及步骤
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
首先,按堆的定义将数组R[0..n]调整为堆(这个过程称为创建初始堆),交换R[0]和R ; 然后,将R[0..n-1]调整为堆,交换R[0]和R[n-1]; 如此反复,直到交换了R[0]和R[1]为止。
以上思想可归纳为两个操作:
(1)根据初始数组去构造初始堆,然后构建一个完全二叉树,根据升序降序需求选择大顶堆或小顶堆。 (2)每次交换第一个和最后一个元素(堆顶元素与末尾元素),将最大元素"沉"到数组末端,输出最后一个元素(最大值) (3) 然后把剩下元素重新调整为大根堆,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。 当输出完最后一个元素后,这个数组已经是按照从小到大的顺序排列了。
下面通过几张示例图,帮助理解!
步骤一 构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
a.假设给定无序序列结构如下
此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整。
找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换。
这时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。
此时,我们就将一个无需序列构造成了一个大顶堆。
步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
将堆顶元素9和末尾元素4进行交换
.重新调整结构,使其继续满足堆定义
再将堆顶元素8与末尾元素5进行交换,得到第二大元素8.
后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
下面演示heap_sort (a, n)对a={20,30,90,40,70,110,60,10,100,50,80}, n=11进行堆排序过程。下面是数组a对应的初始化结构:
1 初始化堆
在堆排序算法中,首先要将待排序的数组转化成二叉堆。
下面演示将数组{20,30,90,40,70,110,60,10,100,50,80}转换为最大堆{110,100,90,40,80,20,60,10,30,50,70}的步骤。
1.1 i=11/2-1,即i=4
上面是maxheap_down(a, 4, 9)调整过程。maxheap_down(a, 4, 9)的作用是将a[4…9]进行下调;a[4]的左孩子是a[9],右孩子是a[10]。调整时,选择左右孩子中较大的一个(即a[10])和a[4]交换。
1.2 i=3
上面是maxheap_down(a, 3, 9)调整过程。maxheap_down(a, 3, 9)的作用是将a[3…9]进行下调;a[3]的左孩子是a[7],右孩子是a[8]。调整时,选择左右孩子中较大的一个(即a[8])和a[4]交换。
1.3 i=2
上面是maxheap_down(a, 2, 9)调整过程。maxheap_down(a, 2, 9)的作用是将a[2…9]进行下调;a[2]的左孩子是a[5],右孩子是a[6]。调整时,选择左右孩子中较大的一个(即a[5])和a[2]交换。
1.4 i=1
上面是maxheap_down(a, 1, 9)调整过程。maxheap_down(a, 1, 9)的作用是将a[1…9]进行下调;a[1]的左孩子是a[3],右孩子是a[4]。调整时,选择左右孩子中较大的一个(即a[3])和a[1]交换。交换之后,a[3]为30,它比它的右孩子a[8]要大,接着,再将它们交换。
1.5 i=0
上面是maxheap_down(a, 0, 9)调整过程。maxheap_down(a, 0, 9)的作用是将a[0…9]进行下调;a[0]的左孩子是a[1],右孩子是a[2]。调整时,选择左右孩子中较大的一个(即a[2])和a[0]交换。交换之后,a[2]为20,它比它的左右孩子要大,选择较大的孩子(即左孩子)和a[2]交换。
调整完毕,就得到了最大堆。此时,数组{20,30,90,40,70,110,60,10,100,50,80}也就变成了{110,100,90,40,80,20,60,10,30,50,70}。
第2部分 交换数据
在将数组转换成最大堆之后,接着要进行交换数据,从而使数组成为一个真正的有序数组。
交换数据部分相对比较简单,下面仅仅给出将最大值放在数组末尾的示意图。
上面是当n=10时,交换数据的示意图。
当n=10时,首先交换a[0]和a[10],使得a[10]是a[0…10]之间的最大值;然后,调整a[0…9]使它称为最大堆。交换之后:a[10]是有序的!
当n=9时, 首先交换a[0]和a[9],使得a[9]是a[0…9]之间的最大值;然后,调整a[0…8]使它称为最大堆。交换之后:a[9…10]是有序的!
…
依此类推,直到a[0…10]是有序的。
最后再看一个示例:
设有一个无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 }。
初始堆:
构造了初始堆后,我们来看一下完整的堆排序处理:
还是针对前面提到的无序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 } 来加以说明。
相信,通过以上两幅图,应该能很直观的演示堆排序的操作处理。
算法分析
堆排序算法的总体情况
假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),需要遍历多少次呢?
堆排序是采用的二叉堆进行排序的,二叉堆就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的定义,它的深度至少是lg(N+1)。最多是多少呢?由于二叉堆是完全二叉树,因此,它的深度最多也不会超过lg(2N)。因此,遍历一趟的时间复杂度是O(N),而遍历次数介于lg(N+1)和lg(2N)之间;因此得出它的时间复杂度是O(N*lgN)。
堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为nlogn。所以堆排序时间复杂度一般认为就是O(nlogn)级。
我们在每次重新调整堆时,都要将父节点与孩子节点比较,这样,每次重新调整堆的时间复杂度变为O(logn),而堆排序时有n-1次重新调整堆的操作,建堆时有((len-1)/2+1)次重新调整堆的操作,因此堆排序的平均时间复杂度为O(n*logn)。由于我们这里没有借用辅助存储空间,因此空间复杂度为O(1)。
堆排序在排序元素较少时有点大才小用,待排序列元素较多时,堆排序还是很有效的。因为其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上,
另外,堆排序在最坏情况下,时间复杂度也为O(n*logn)。相对于快速排序(平均时间复杂度为O(n*logn),最坏情况下为O(n*n)),这是堆排序的最大优点。
算法稳定性
● 堆排序是不稳定的算法,它不满足稳定算法的定义。它在交换数据的时候,是比较父结点和子节点之间的数据,所以,即便是存在两个数值相等的兄弟节点,它们的相对顺序在排序也可能发生变化。
算法稳定性的定义 – 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!
相关文章推荐
- 【初探】 堆排序
- Redis 启动与关闭初探
- 堆与堆排序
- SQLStory摘录(二)————联接查询初探
- windowsCE异常和中断服务程序初探(-)
- java Fork/join 初探
- 堆排序(Heap Sort)
- 使用 WebSphere Extended Deployment Compute Grid 进行批处理编程之初探
- OpenStreetMap初探(外一篇)——Serving Tiles
- cpu缓存java性能问题初探
- 线程池初探
- 大型Web2.0站点构建技术初探
- springMVC3.0后台验证信息不显示的原因初探
- WebKit之Chromium的UI绘制流程初探
- 服务端测试之接口测试初探
- [转]大型Web2.0站点构建技术初探
- 管道文件初探
- 手把手入门神经网络系列(1)_从初等数学的角度初探神经网络
- 初探无线安全审计设备WiFi Pineapple Nano系列之PineAP
- 初探WCF 如何在配置文件中指定Address?