代码改变世界

堆排序

2015-11-17 00:12  yrpapa  阅读(93)  评论(0)    收藏  举报

一 定义

n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
(1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2),当然,这是小根堆,大根堆则换成>=号。//k(i)相当于二叉树的非叶子结点,K(2i)则是左子节点,k(2i+1)是右子节点
若将此序列所存储的向量R[1..n]看做是一棵完全二叉树存储结构,则堆实质上是满足如下性质的完全二叉树:
树中任一非叶子结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
【例】关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆,其对应的完全二叉树分别如小根堆示例和大根堆示例所示。
大根堆和小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。注意:①堆中任一子树亦是堆。②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k叉堆。
 
二 高度
堆可以被看成是一棵树,结点在堆中的高度可以被定义为从本结点到叶子结点的最长简单下降路径上边的数目;定义堆的高度为树根的高度。我们将看到,堆结构上的一些基本操作的运行时间至多是与树的高度成正比,为O(lgn)。
 
三 大根堆排序算法的基本操作
①建堆,建堆是不断调整堆的过程,从len/2处开始调整,一直到第一个节点,此处len是堆中元素的个数。建堆的过程是线性的过程,从len/2到0处一直调用调整堆的过程,相当于o(h1)+o(h2)…+o(hlen/2) 其中h表示节点的深度,len/2表示节点的个数,这是一个求和的过程,结果是线性的O(n)。
②调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和它的孩子节点left(i),right(i),选出三者最大(或者最小)者,如果最大(小)值不是节点i而是它的一个孩子节点,那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,因为是沿着深度方向进行调整的。
③堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根节点取出(一般是与最后一个节点进行交换),将前面len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。堆排序过程的时间复杂度是O(nlgn)。因为建堆的时间复杂度是O(n)(调用一次);调整堆的时间复杂度是lgn,调用了n-1次,所以堆排序的时间复杂度是O(nlgn)
 
四 代码
//最小堆排序和最大堆排序

#include <algorithm>
#include <functional>
#include <vector>
#include <iostream>
using namespace std;

template<typename Type>
class Heap
{
public:
    Heap(const vector<Type>& a_array)
    {
        m_array.assign(a_array.begin(),a_array.end());
    }

    template<typename Compare>
    void sort(Compare comp);

    void printArray(const vector<Type>& a_array);

private:
    vector<Type> m_array;

    //comp 为less<Type> 则大数下沉,创建最小堆,从小到大排序
    //comp 为greater<Type> 则小数下沉,创建最大堆,从大到小排序
    template<typename Compare>
    void creatHeap(Compare comp);                //创建堆

    template<typename Compare>
    void downElement(int a_elem, Compare comp);    //下沉元素
};

template<typename Type>
template<typename Compare>
void Heap<Type>::sort(Compare comp)
{
    printArray(m_array);
    creatHeap(comp);                    //建堆
    vector<Type> array;
    for (int i = m_array.size() - 1; i >= 0; i--)
    {
        array.push_back(m_array[0]);    //保留堆顶
        swap(m_array[0], m_array[i]);    //交换
        m_array.pop_back();                //去掉最后一个元素
        downElement(0,comp);            //将新的首元素下沉
    }
    printArray(array);
    m_array.assign(array.begin(),array.end());
}

template<typename Type>
template<typename Compare>
void Heap<Type>::creatHeap(Compare comp)
{
    //从最后一个非叶子节点开始,将每个父节点都调整为最小堆
    for (int i=m_array.size()/2-1; i>=0; i--)
    {
        downElement(i, comp);
    }
}

template<typename Type>
template<typename Compare>
void Heap<Type>::downElement(int a_elem, Compare comp)    //下沉元素
{
    int min;            //设置最小元素下标
    int index = a_elem;    //当前下沉的元素下标
    while (index*2+1 < m_array.size())//存在左节点
    {
        min = index*2+1;
        if (index*2+2 < m_array.size())//存在右节点
        {
            //左右节点比较,选出最小的
            if (comp(m_array[index*2+2],m_array[min]))
            {
                min = index*2+2;
            }
        }
        //同子节点比较,若父节点最小则结束
        if (comp(m_array[index],m_array[min]))
        {
            break;
        }
        else//选最小元素到父节点
        {
            swap(m_array[min],m_array[index]);
            index = min;
        }
    }
}

template<typename Type>
void Heap<Type>::printArray(const vector<Type>& a_array)
{
    for (int i=0; i<a_array.size(); i++)
    {
        cout << a_array[i] << " ";
    }
    cout << endl;
}


int main()
{
    vector<int> array;
    for (int i=10; i<20; i++)
    {
        array.push_back(i);
    }
    random_shuffle(array.begin(), array.end());//打乱顺序
    Heap<int> heap(array);
    heap.sort(less<int>());
    heap.sort(greater<int>());
    return 0;
}