堆排序

堆排序

堆的基础知识我们已经在《堆的基础知识》: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构建成最大堆
技术分享
技术分享
技术分享

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


堆排序操作

给定我们一个最大堆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)

技术分享
技术分享

代码实现

/*************************************************
* @Filename:    heapSort.cc
* @Author:      qeesung
* @Email:       [email protected]
* @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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。