常用排序算法之——堆排序

堆与堆排序的原理,参考该博客: 白话经典算法系列之七 堆与堆排序

 

二叉堆是个完全二叉树,可以用一个数组来保存节点,不会浪费空间,能快速定位;本人用一个vector来代替数组,省去自己对内存的分配/重分配和释放等操作。

 

堆排序就是每次取出小顶堆的堆顶并输出,然后对堆重新调整。

 

堆排序需要对数组先建堆【时间复杂度为O(N):高度为h的层最多有n/2h+1个结点,每个结点上调的复杂度为O(h),求累加和,取极限值,求出复杂度为O(3N)】,调整的过程中会打破元素的相对顺序,是一种不稳定的排序算法。时间复杂度最好、最坏、平均都为O(N logN)。

 

本人实现的代码,一个小顶堆,作为笔记:

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

template<class T>
class MyHeap
{
public:
    // MyHeap()    //默认构造函数对vector初始化
    // ~MyHeap()
    void init(T nums[], int n);  //用一个数组来初始化,并建好堆
    int  size();           //返回堆中元素个数
    void add(T new_item);  //添加一个元素
    T    top();        //返回堆顶(最小值)
    void pop();        //删除堆顶,不返回

private:
    inline void fixUp(int i);   //堆的向上调整
    inline void fixDown(int i);  //堆的向下调整

    vector<T> m_tv;  //采用vector数据结构来保存元素
};

template<class T>
void MyHeap<T>::init(T nums[], int n)
{
    m_tv.resize(n);
    memcpy(&m_tv[0], nums, n * sizeof(T));

    if(n < 2)
    {
        return;
    }

    for(int i = (n - 2) >> 1; i >= 0; --i)  //从第一个非叶节点开始向下调整
    {
        fixDown(i);
    }
}

template<class T>
int MyHeap<T>::size()
{
    return m_tv.size();
}

template<class T>
void MyHeap<T>::add(T new_item)
{
    m_tv.push_back(new_item);
    fixUp(m_tv.size() - 1);  //添加到数组尾部,然后向上调整
}

template<class T>
T MyHeap<T>::top()
{
    if(m_tv.empty())
    {
        cout << "head empty" << endl;
        return -1;
    }
    return m_tv[0];  //返回堆顶
}

template<class T>
void MyHeap<T>::pop()
{
    if( m_tv.empty() )
    {
        return;
    }
    m_tv[0] = m_tv.back();  //将最后一个元素与堆顶交换,然后删除最后一个元素(变相删除第一个元素)
    m_tv.pop_back();
    fixDown(0);      //从堆顶开始向下调整
}

template<class T>
inline void MyHeap<T>::fixUp(int i)
{
    //从叶子到根节点的路径上,把叶子调整到大于它的元素之上
    for(int j = (i-1) >> 1; (j >= 0) && (i != 0) && (m_tv[j] > m_tv[i]); i = j, j = (i-1) >> 1)
        swap(m_tv[i], m_tv[j]);
}

template<class T>
inline void MyHeap<T>::fixDown(int i)
{
    const int n = m_tv.size();
    int j = 2 * i + 1;
    while(j < n)
    {
        if(j+1 < n && m_tv[j+1] < m_tv[j])  //找出两个儿子中的较小值
        {
            ++j;
        }

        if(m_tv[i] < m_tv[j])  //如果小于儿子的值,说明已经在合适的位置
        {
            break;
        }

        swap(m_tv[i], m_tv[j]);  //和小儿子交换,直至找到比他大的儿子或者成为叶节点
        i = j;
        j = 2 * i + 1;
    }
}

int main(int argc, char *argv[])
{
    int    ia[] = {4, 2, 1, 10, 3, 8};
    double da[] = {4.13, 2.11, 1.1, 10.19, 3.12, 8.17};

    MyHeap<int> ih;    //int元素的堆
    MyHeap<double> dh;  //double元素的堆
    
    ih.init(ia, sizeof(ia)/sizeof(*ia));
    ih.add(5);
    const int in = ih.size();
    cout << in << endl;
    for(int i = 0; i < in; ++i)
    {
        cout << ih.top() << ' ';
        ih.pop();
    }
    cout << endl;

    dh.init(da, sizeof(da)/sizeof(*da));
    dh.add(5.14);
    dh.add(7.16);
    const int dn = dh.size();
    cout << dn << endl;
    for(int i = 0; i < dn; ++i)
    {
        cout << dh.top() << ' ';
        dh.pop();
    }
    cout << endl;

    return 0;
}

 

posted @ 2014-08-07 11:27  阿杰的专栏  阅读(318)  评论(0编辑  收藏  举报