堆排序——HeapSort

来源:互联网 发布:php 实例化对象的本质 编辑:程序博客网 时间:2024/05/20 15:56

堆排序:

堆排序与归并排序的时间复杂度一样,同为O(nlogn),但与归并排序不同的是,堆排序具有空间原址性:任何时候都只需要常数个额外的空间存储临时数据。

堆的定义:

(二叉)堆是一个数组,可以被看成是一个近似的完全二叉树。树上的每个结点对应数组中的一个元素。除了最顶层外,树应该是充满的,且为从左向右填充。

二叉堆通常分为两种:最大堆和最小堆。
最大堆:堆中的最大元素存放在根节点中,并且在任何一个子树中,该子树所包含的所有结点的值都不大于该子树的根结点的值。最大堆性质如下:

            A[i / 2] >= A[i]

最小堆的性质与最大堆正好相反:

            A[i / 2] <= A[i]

要使用堆排序,就要熟悉堆的基本操作:维护堆的性质(最大堆为例)。

给性一个数组A和其一个下标i,在维护堆的性质时,i的左孩子或右孩子有可能大于i结点的值,这样就不符合堆的性质了。要维护最大堆的性质,就要让A[i]的值在最大堆中“逐级下降”,使得以下标为i为根结点的子树重新具有最大堆的性质。

下面是维护最大堆过程:

/* length为数组是长度,i为最后一个非叶子结点 */void Max_Heapify(int A[], int length, int i)//维护最大堆{    int left = 2 * i;    int right = 2 * i + 1;    int largest;  //largest暂存值最大的节点的下标    if(left < length && A[left] > A[i])        largest = left;    eles        largest = i;    if(right < length && A[right] > A[largest])//选出结点值最大的下标        largest = right;    if(i != largest)//若最大值不为根结点,则把最大值交换至根节点处        swap(&A[largest], &A[i]);        Max_Heapify(A, length, largest);//继续维护以largest为根结点的子树}

有了维护最大堆的过程,下面就可以由给出的数组来建一个最大堆了。字数组A([n / 2]…n)中的元素都是叶子结点,每个叶子结点可以看成只包含一个元素的堆。

void BuildHeap(int A[], int length){    int i;    for(i = length / 2; i >= 1; i--)//从最后一个非叶子结点开始建最大堆        Max_Heapify(A, length, i);}

开始时,堆排序算法利用Max_Heapify将输入的数组A[]建成最大堆,由于数组中的最大元素总是存放在根结点A[1]中,通过让它与A[n]互换,可以将最大值放在正确的位置。接下来,将n结点去掉,n的孩子结点仍然满足最大堆的性质,而新的根结点不一定满足,所以就要调用Max_Heapify()来调整新的根节点。以使其满足最大堆性质,从而在A[1…n-1]上新建一个最大堆。重复这一过程,直至堆的大小从n - 1降到2.

代码如下:
void heap_sort(int A[], int length){    int size = length;    BuildHeap(A, size);     //建立最大堆    while(size > 1)    {        swap(&A[size - 1], &A[1]);   //每次将最大的值放到正确的位置        size--;        Max_Heapify(A, size, 1);  //将剩下的元素重新建堆    }}
0 0