堆排序

来源:互联网 发布:广告联盟知乎 编辑:程序博客网 时间:2024/05/17 22:11

堆排序

堆的基础知识我们已经在《堆的基础知识》:http://blog.csdn.net/ii1245712564/article/details/45505799里面介绍过了,这次我们将介绍堆的用途之一:堆排序

在诸多的排序算法里面里面,堆排序算是比较快速的了,排序时间消耗为:O(nlogn),虽然相对于快速排序来说慢了一点点,但是就对于堆的特有性质而言(最大堆堆顶元素为最大元素,最小堆堆顶元素为最小元素),堆还是有比较多的用途的,这个我们会在后面的文章里面介绍。

我们这里给出一个堆排序的基本思路:

Created with Raphaël 2.1.2开始建堆维护堆的性质堆大小小于2?结束交换堆顶元素和堆末尾元素堆大小减去1yesno

这里我们就按照上面的流程图思路来对每一步骤进行构建

建堆

加入给定我们一个数组A={a1,a2,a3,...,an},数组A里面的元素是任意无序的,现在要将这些该数组进行堆的构建,我们需要怎么做?因为最大堆和最小堆比较类似,所以我们这里采用最大堆来讲解。
还记得我们在文章《堆的基础知识》里面提到过一个维护堆性质的函数MAX-HEAPIFY(A,i)吗?MAX-HEAPIFY(A,i)这个函数是在节点i的左子树和右子树均满足最大堆的性质下进行的。
但是我们在数组A里面除了叶节点以外,我们都不能保证每一棵子树都是具有最大堆的性质的,于是我们现在需要从叶节点的父节点开始,自下而上的维护最大堆的性质,因为PARENT(i)是一定小于i的,所以在我们用MAX-HEAPIFY(A,i)维护节点i的性质的时候,其子树是一定具有最大堆的性质的,因为其子树LEFTCHILD(i)RIGHTCHILD(i)的最大堆性质是早于其父节点i构建的,且其运行时间为O(logn)
下面我们给出建堆的伪代码:

BUILD-MAX-HEAP(A)  for i = A.heapSize/2 downto 1        MAX-HEAPIFY(A,i)

于是我们就可以将数组A构建成最大堆
Alt text
Alt text
Alt text

好了,在我们构建出最大堆以后,我们需要做的就是来进行堆排序的核心操作啦!


堆排序操作

给定我们一个最大堆A,要我们对这个最大堆进行排序,因为最大堆/最小堆的堆顶元素永远是整个堆里面最大/最小的,于是我们不妨将整个堆顶的元素提出来,然后将堆尾部的元素放入堆顶,对堆顶1进行堆性质的维护,之后堆顶元素就成了剩下元素里面最大的,再次提出该元素,重复上面的动作,直到堆只剩下一个元素即可,这个就是堆排序的核心思想了。
为了怎么空间的可重用性,我们不妨将堆顶元素和堆尾部的元素交换,然后让堆大小减小一,当整个操作完毕后,数组A就是已经有序的了!
下面我们用伪代码实现:

HEAPSORT(A)  BUILD-MAX-HEAP(A)  while A.heapSize > 1      exchange(A,A.heapSize,1)      A.heapSize = A.heapSize-1      MAX-HEAPIFY(A,1)

Alt text
Alt text

代码实现

/************************************************** @Filename:    heapSort.cc* @Author:      qeesung* @Email:       qeesung@qq.com* @DateTime:    2015-05-06 10:32:10* @Version:     1.0* @Description: 堆排序**************************************************/#include <iostream>using namespace std;// 二叉树的基本操作#define PARENT(i) ((i)>>1)#define LEFTCHILD(i) ((i)<<1)#define RIGHTCHILD(i) (((i)<<1)+1)void printArray(int * array);/** * 交换数组两个位置的元素 * @param array 数组 * @param pos1  位置1 * @param pos2  位置2 */void exchange(int * array , int pos1 , int pos2){    if(array == NULL)        return;    int temp = array[pos1];    array[pos1] = array[pos2];    array[pos2] = temp;}/** * 维护最大堆的性质 * @param array 传入的堆数组,注意此数组是从位置1开始,位置0存放堆的大小 * @param pos   需要更新的位置 */void maxHeapify(int * array , int pos){    if(array == NULL)        return;    if(pos > array[0])// 超出堆的大小        return;    int leftChild = LEFTCHILD(pos);    int rightChild = RIGHTCHILD(pos);    // 找到最大的那个    int maxPos = pos;    if(leftChild <= array[0] && array[leftChild] > array[pos] )        maxPos = leftChild;    if(rightChild <= array[0] && array[rightChild] > array[maxPos])        maxPos = rightChild;    // 交换父节点与子节点    if(maxPos != pos)    {        exchange(array , maxPos , pos);        maxHeapify(array , maxPos);    }}/** * 堆排序 * @param array 传入需要排序的数组,注意此数组是从位置1开始,位置0存放数组数组元素的个数    */void heapSort(int * array){    if(array == NULL)        return;    // 首先构建最大堆    for(int k = array[0]/2 ; k >=1 ; --k)    {        maxHeapify(array , k);    }    int arraySize = array[0];    //下面开始每次都取出堆里面堆顶位置的元素,再次进行堆性质维护    for(int k = array[0] ; k >=2 ; --k)    {        exchange(array , 1, k);        array[0] -= 1;        maxHeapify(array , 1);    }    array[0] = arraySize;}void printArray(int * array){    if(array == NULL)        return;    int arraySize = array[0];    for(int k = 1 ; k <= arraySize ; ++k)    {        cout<<array[k]<<"\t";    }    cout<<endl;}int main(int argc, char const *argv[]){    int array[]={0,4,1,3,2,16,9,10,14,8,7};    array[0] = sizeof(array)/sizeof(int) - 1;    cout<<"before sort:"<<endl;    printArray(array);    heapSort(array);    cout<<"after sort:"<<endl;    printArray(array);    return 0;}

运行结果为:

before sort:
4 1 3 2 16 9 10 14 8 7
after sort:
1 2 3 4 7 8 9 10 14 16

1 0
原创粉丝点击