标签:style blog http ar io os sp for java
那么首先要解决的问题就是给数组排序,如何转换成二叉树的?转换方法如图:
数组 int a[],包含元素a[0],a[1],a[2],a[3].....等等。
转换成二叉树:
图还是比较形象的,其实就是依次从堆顶向下排,但是有一个原则,就是上一层没有排满的时候,下一层不会有元素,有限排满上一层,才考虑下一层。
那么第二个问题来了,我们怎么才能对其进行排序呢。
在考虑这个问题之前,我们需要先进行一些前序工作:
通过观察不难得出,如果像我们上图这样,数组序号从0开始排起的话,元素a[i]的父节点(当然i不等于0时)是a[(i+1)/2 - 1],而左右子节点分别是a[2*i+1]和a[2*i+2];
大 根堆:除根节点以外的所有及诶单a[i]都要满足:a[PARENT(i)]>=a[i],这里parent(i)表示i的父节点的下标值;显然如 果我们将一个树变成了一个大根堆,我们就可以得到一个当前树的最大值,也就是这个堆的顶部节点的值,由大根堆的性质可知。
显然,如果根节点比左右子节点要大,那么已经是大根堆
如果根节点比左右子节点要小,或者比其中一个要小?我们就需要将最大的那个子节点与根节点交换位置;但是这还没有完,因为原根节点交换到它的直接子节点位置之后,还不一定会满足大根堆的条件限制,如果不满足,我们就需要继续刚才的工作;
看看下面的代码:
经 过第3点,已经搞定了在左右子树都是大根堆的情况下,形成一个新的大根堆;那么我们如何让一颗没有处理过的堆来形成一个大根堆呢?如何找到左右子树都是大 根堆的情况呢?找不到,那么不如先不要左右子树,那就是叶子节点,叶子节点没有左右子树,换言之它们就是天然的单节点大根堆;所以叶子节点的父节点都是满 足3所假设的条件的,那么对于一个堆而言,叶子节点的父节点的下标的范围是多少呢?
对 于一个满节点二叉树而言,如果有h层,比如上图中到a[15]处,有三层;对于h层的满节点二叉树,它的节点个数是2*2^h-1,2^h即2的h次幂, 叶子节点的个数为2^h,而除叶子节点之外的其他节点的个数为2^h-1个。我们不难得出,如果数组下标从0开始算起的话,除叶子节点以外的其他节点的下 标范围应该是[0,2^h-2]。
所 以我们只需要从2^h-2位置的节点开始,也就是倒数第二层的最右边的节点开始。而对于高为h层,节点数为n的堆而言,h和n有如下关系:h=不小于 lgn的最接近的整数;知道了上面这些,我们就可以从2^h-2位置开始一直到0位置一直循环执行第3点来实现形成一个大根堆:
经过上面四步,我们已经可以得到一个大根堆了,接下来的工作就是如何利用大根堆来进行排序。
对 于大根堆而言,我们唯一可以确定的是,它的顶部的那个元素一定是当前堆中最大的元素,于是我们可以每次将顶部的元素和最右边的叶子节点也就是最后一个叶子 节点进行交换,然后拿出这个最大值,并且将堆的节点数减一,然后重新对顶点也就是刚才那个交换到顶点位置的叶子节点进行上面3、中的操作,来重新让新堆变 成一个大根堆,然后我们依次循环执行上面的操作,直到取出堆中的所有元素,这样,我们从开始到最后取出的元素是按照从大到小的顺序,也就完成了排序。
这一步的代码很简单:
对于堆排序算法,我们简单的看一下它的时间复杂度,一次maxHeapBuild的时间复杂度是n,然后循环n-1的maxHeapIndex时间复杂度为nlogn,循环赋值操作复杂度为n,所以算起来,时间复杂度为nlogn。
标签:style blog http ar io os sp for java
原文地址:http://www.cnblogs.com/yuyanbian/p/4167549.html