堆排序

Posted Curo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆排序相关的知识,希望对你有一定的参考价值。

 堆排序是由1991年的计算机先驱奖获得者、斯坦福大学计算机科学系教授罗伯特.弗洛伊德(Robert W.Floyd)和威廉姆斯(J.Williams)在1964年共同发明了的一种排序算法( Heap Sort );

        堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

基本介绍

先直观感受一下,下面就是一个堆:

20 17 8 7 16 3

什么??上面不就一个数组吗……?!

没错,(二叉)堆数据结构是一种数组对象。

不过,让我们用另外一种方式来看这个数组:

对于表示堆的数组arr[0…n-1],我们以arr[0]为根,给定某个节点下标i,令其父节点和左右后代节点的下标为:

parent(i) = (i-1)/2;

left(i) = 2*i+1;

right(i) = 2*i+2;

(具体实现时,可用移位来实现乘以2和除以2)

于是,它可以看作一棵完全二叉树:

技术分享图片

可是,这也只是一棵完全二叉树,有啥特别之处呢?

特点就是:除根节点以外的每个节点i,都有arr[ parent(i) ] >= arr[i]。

堆分为最大堆和最小堆,上面就是最大堆,最小堆的特点则是:除根节点以外的每个节点i,都有arr[ parent(i) ] <= arr[i]。

堆排序一般使用最大堆,最大堆中的最大元素位于根节点。

因为具有n个元素的堆是基于一颗完全二叉树的,所以其高度为O(log n)。

算法分析

        其实这种算法看起来挺复杂,但是如果真正理解了就会感觉非常简单的;

        基本思想:把待排序的元素按照大小在二叉树位置上排列,排序好的元素要满足:父节点的元素要大于等于其子节点;这个过程叫做堆化过程,如果根节点存放的是最大的数,则叫做大根堆;如果是最小的数,自然就叫做小根堆了。根据这个特性(大根堆根最大,小根堆根最小),就可以把根节点拿出来,然后再堆化下,再把根节点拿出来,,,,循环到最后一个节点,就排序好了。

        基本步骤:

        其实整个排序主要核心就是堆化过程,堆化过程一般是用父节点和他的孩子节点进行比较,取最大的孩子节点和其进行交换;但是要注意这应该是个逆序的,先排序好子树的顺序,然后再一步步往上,到排序根节点上。然后又相反(因为根节点也可能是很小的)的,从根节点往子树上排序。最后才能把所有元素排序好;

保持堆的性质

在清楚什么是最大堆之后,我们来谈一谈如何保持堆的性质,也就是说,如果堆中有节点不满足堆的性质,我们如何进行调整。

首先,我们假定以节点i的左右儿子为根的两棵二叉树都是最大堆,而以节点i为根的二叉树可能不是最大堆,则调整的过程如下:

  1. 从元素arr[i], arr[left(i)], arr[right(i)]中找出最大的元素,将下标存在largest中;
  2. 如果arr[i]是最大的,说明以节点i为根的二叉树是最大堆,无须调整,程序结束;否则,交换arr[i]和arr[largest],于是arr[i], arr[left(i)], arr[right(i)]三者满足了最大堆的性质,但是交换后,下标为largest的节点存放arr[i]的值,以该节点为根的子树又可能违反最大堆的性质,因此需要对该子树递归调用本调整过程。

 

时间复杂度

 

以上是关于堆排序的主要内容,如果未能解决你的问题,请参考以下文章

选择排序(简单选择排序堆排序的算法思想及代码实现)

排序--08---堆排序

python代码实现堆排序

算法-java代码实现堆排序

一文带你了解堆排序

堆排序