讲到堆排序,我们首先要有一个对堆的理解,知道什么是堆,什么是大顶堆,什么是小顶堆。
什么是堆?
堆是一个非常不好理解的概念,我在这里尽量使用通俗易懂的语言去解释它。
堆,故名思义,就是把一堆东西堆在一起,例如稻谷堆在一起就要做谷堆,木材堆在一起就叫做木堆,它们有一个共同的现象就是上箭下大的呈锥形。
如果我们把数据按照一定的规则堆在一起,那么就是计算机科学中的堆了。
下面就是堆的定义了(引用自百度百科)
堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。
利用数学语言来描述就是:
如果序列被称作堆,
当且仅当其满足(ki= k2i,ki>= k2i+1), (1
大顶堆和小顶堆
顾名思义,堆的顶点,也即二叉树的根或者数组的首项时所有数据中最小的那一个数,这样的堆就被称作小顶堆,反之就称作大顶堆。
显然,一个已经排好序的序列必然时一个堆,反之,一个堆虽然不是一个排好序的序列,但是却是一个基本有序的序列。
那么,如果我们能找到一个合适的办法,将无序的序列转化成堆,然后在将堆转化成一个完全有序的序列,也许我们就可以发现一个还不错的排序算法。
如何把一个混乱的序列变成一个堆?
一个堆,从其根节点(顶点)到叶子的任何一条路径,在本质上讲都是一个有序序列。要让一个混乱的序列变成一个堆,我们可以类比与冒泡排序的方法,从叶子节点开始,让比较小的数像气泡一样向上浮动,那么到根节点的时候,我们就非常方便的得到了一个小顶堆,其顶点,也就是二叉树的根节点,或者数组的第一个元素就是最小的元素。
也可以换一个思路来理解这个想法,假设一个二叉树,除了根节点,左子树和右子树都已经是一个小顶堆了,下面如何把根节点移动到合适的位置使得这个二叉树也变成小顶堆。这个过程其实有点类似于插入排序,将一个新的元素插入到已经排好序的数组中,我们只需要将其跟尾端的元素依次比较,直到找到其适合的位置。
代码中的bubbleup(heap, pos)就可以用两种不同的思路分别来理解,如果把它看成冒泡的过程,其实就是将叶子和父节点比较,保证较小的叶子节点能够向上浮动到合适的位置。
如果理解成插入的思路,我们可以把pos节点所在的两个子树都已经是堆了,我们剩下要做的就是把pos节点的元素插入到合适的位置,使得从pos开始向下的整个树都是小顶堆。
如何从一个堆变成排好序的数组?
不妨设我们有一个小顶堆,我们把堆顶的元素给提取出来,也就是我们得到了最小的元素。然后我们把数组的最后一个元素移动到堆顶,根据堆的定义,我们发现,除了堆顶的元素,剩下所有的元素仍然符合堆的定义,于是我们只需要按照上面介绍的过程,将堆顶的元素插入到合适的位置,我们就可以得到一个新的堆,重复调用上面的过程,我们就可以得到一个完全有序的序列了。
代码(heapsort.py)
堆的应用
堆可以用在分布式或者并行排序的过程中最后多个有序数组的归并处理中。
归并排序中的归并操作比较适合于两个有序数组的归并,但是堆非常适合于多个有序数组的归并。
领取专属 10元无门槛券
私享最新 技术干货