堆排序(Heap Sort)

维基百科:http://zh.wikipedia.org/wiki/堆排序

1、基本知识

二叉堆一般用数组来表示,我们可以把它视为一颗完全二叉树。数据结构中学习树这一章节时,我们知道完全二叉树可以用数组来存储。

堆的定义:

(1)大根堆:父节点的值大于等于孩子结点的值

(2)小根堆:父节点的值小于等于孩子结点的值

父节点和孩子结点下标的计算

(1)假设用数组A[1..n]来存储完全二叉树,即下标从1开始

  • 结点i的父节点:i/2 (向下取整)
  • 结点的左孩子结点:2*i(如果存在)
  • 结点的右孩子结点:2*i + 1(如果存在)

(2)假设用数组A[0..n-1]来存储完全二叉树,即下标从0开始

  • 结点i的父节点:(i-1)/2
  • 结点i的左孩子结点: 2 * i +1(如果存在)
  • 结点的右孩子结点:2 * i + 2(如果存在)

2、算法思想

以对数组进行升序排序为例,可以把数组建为一个大根堆,然后不断输出堆顶元素, 并调整堆,直至输出所有元素。这里采取的是数组首元素和尾元素交换,也就是把最大元置于数组尾部,每一次都可以确定一个元素的最终位置,显然堆排序算法属于原地排序。

3、代码实现

下面的代码是根据算法导论中的伪代码编写的,代码中有详细的注释。

#include <cstdlib>
#include <iostream>

using namespace std;

//父节点下标 
int parent(int i)
{
    return (i - 1)/2;
}

//左孩子下标 
int leftChild(int i)
{
    return (2 * i + 1);
}

//右孩子下标 
int rightChild(int i)
{
    return (2 * i + 2);
}

/*调整下标为i的结点的位置,使以其为根的子树为大根堆
 假定该函数被调用时,以left(i)和right(i)为根的两棵二叉树都是大根堆
*/
template<class T>
void maxHeapify(T* a, int i, int heapSize)
{
    int largest = -1;           //记录a[i]和其子女(如果存在)中最大者小标 
    int left = leftChild(i);
    int right = rightChild(i);
    
    if(left < heapSize && a[left] > a[i])
    {
        largest = left;
    }
    else
    {
        largest = i;
    }
    
    if(right < heapSize && a[right] > a[largest])
    {
        largest = right;
    }
    
    if(i != largest)
    {
        swap(a[i], a[largest]);
        maxHeapify(a,largest,heapSize);
    }
}

//把一个数组构建为大根堆 
template<class T>
void buildMaxHeap(T* a, int heapSize)
{
    int i = heapSize/2 -1;          //最后一个非叶子结点下标 
    for(; i>=0; i--)
    {
        maxHeapify(a, i, heapSize);//从最后一个非叶子结点开始自底向上调整堆 
    }
}

//堆排序 
template<class T>
void heapSort(T* a, int n)
{
    int heapSize = n;
    buildMaxHeap(a, heapSize);//  
    // 把最大元素(堆顶元素)和堆尾元素交换,堆的大小减1,从堆顶开始自顶向下调整堆
    //重复以上操作直到堆的大小为1 
    for(int i=n-1; i>0; i--)
    {
        swap(a[0],a[i]);
        heapSize--;
        maxHeapify(a,0,heapSize);
    }
}


int main(int argc, char *argv[])
{
    int n;
    int *a = NULL;
    
    while(cin>>n && n > 0)
    {
        a = new int[n];
        for(int i=0; i<n; i++)
        {
            cin>>a[i];
        }
        heapSort(a,n);
        for(int i=0; i<n; i++)
        {
            cout<<a[i]<<" ";
        }
        cout<<endl<<endl;
        delete [] a;
    }
    
    system("PAUSE");
    return EXIT_SUCCESS;
}
4、复杂度分析

(1)对于大小为n的数组,堆的高度为lgn,

(2)调整函数maxHeapify的复杂度为O(h),即O(lgn)。

(3)heapSort中循环执行O(n)次,因而算法复杂度为O(nlgn)

算法时最坏情况下和平均情况下的时间复杂度都为O(nlogn),堆排序是渐进最优的排序算法。


参考资料:

[1]算法导论(第2版)

[2]严蔚敏《数据结构(C语言版)》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值