堆知识总结

来源:互联网 发布:terminal mac 编辑:程序博客网 时间:2024/06/06 18:49

堆是一种完全二叉树,堆结构的二叉树存储方式分为大堆和小堆。

大堆:每个父节点都大于孩子节点

小堆:每个父节点都小于孩子节点

创建一个大堆:向下调整算法

思路:我们知道堆均是完全二叉树,因此一个父节点的子节点即child = parent*2+1;因此在向下调整算法中从根节点开始逐一与它的孩子进行比较,每次保证一棵树最大数据是根节点。

例如:int a [] = {10,11, 13, 12, 16, 18, 15, 17, 14, 19};

用完全二叉树表示:这里写图片描述

Heap(T* array, size_t sz)   //大堆{    for (int i = 0; i < sz;++i)    {        _array.push_back(array[i]);    }    for (int j = (_array.size()-1) / 2; j>=0; --j)    {        _Adjustdown(j);    }}void _Adjustdown(int root)   //向下调整算法{    size_t parent = root;    size_t child = parent * 2 + 1;    while (parent < _array.size())    {        if ((child+1)<(_array.size())&&_array[child+1] > _array[child])        {            ++child;        }        if ((child<_array.size())&&_array[child] > _array[parent])        {            swap(_array[child], _array[parent]);            parent = child;     //继续向下调整            child = parent * 2 + 1;        }        else        {            break;        }    }}

同理,我们创建一个小堆,使用向上调整算法:

例如:int a [] = {10,11, 13, 12, 16, 18, 15, 17, 14, 19};

将其转化为小堆模式代码:

void _AdjustUp(int root){    size_t parent = root;    size_t child = parent * 2 + 1;    while (parent < _array.size())    {        if ((child + 1)<(_array.size()) && _array[child + 1] < _array[child])        {            ++child;        }        if ((child<_array.size()) && _array[child] < _array[parent])        {            swap(_array[child], _array[parent]);            parent = child;     //继续向下调整            child = parent * 2 + 1;        }        else        {            break;        }    }}

转化后为:这里写图片描述

堆的删除:堆一般进行pop()操作,删除堆顶元素:

思路:如果我们直接进行删除数组第一个元素_array[0],那么还必须进行堆排序操作,时间复杂度较大,但是我们换个思路,要删除第一个数,由于第一个数变化对整个数组均影响,因此我们可以先把第一个和最后一个调换位置,然后删除最后一个,最进行向下调整算法(大堆)或向上调整算法(小堆)即可。

这里写图片描述

代码如下:

void Pop(){    assert(!_array.empty());    swap(_array[0], _array[_array.size() - 1]);    _array.pop_back();    _Adjustdown(0);}

堆的插入:插入算法就是把一个节点插入到堆的最后,然后进行向上或向下调整算法

void Push(const T& x){    _array.push_back(x);    _Adjustdown(0);}

但是我们发现在进行大堆或小堆操作时,代码差不多都相同,只有少数几个判断大小的符号不同,这样也不易维护,因此增加代码简洁型型=性,我们可采用仿函数进行优化。

通过仿函数,我们只需要传入一个模板参数,然后通过创建的对象就可以实现控制是大堆还是小堆。

源代码:

//需要小堆template <class T>struct Less{    bool operator()(const T& x1, const T& x2)    {        return x1 < x2;    }};//当需要大堆时template <class T>struct Greater{    bool operator ()(const T& a, const T& b)    {        return a > b;    }};template<class T, class compare = Greater<T>>class Heap{public:Heap(){}Heap(T* array, size_t sz)   //大堆{    for (size_t i = 0; i < sz;++i)    {        _array.push_back(array[i]);    }    for (int j = (_array.size()-1) / 2; j>=0; --j)    {        _Adjustdown(j);    }}void Pop(){    assert(!_array.empty());    swap(_array[0], _array[_array.size() - 1]);    _array.pop_back();    _Adjustdown(0);}void Push(const T& x){    _array.push_back(x);    _Adjustdown(0);}protected:void _Adjustdown(int root)   //向下调整算法{    size_t parent = root;    size_t child = parent * 2 + 1;    while (parent <_array.size())    {        if ((child + 1)<(_array.size()) && com(_array[child + 1] , _array[child]))        {            ++child;        }        if ((child<_array.size())&&com(_array[child] , _array[parent]))        {            swap(_array[child], _array[parent]);            parent = child;     //继续向下调整            child = parent * 2 + 1;        }        else        {            break;        }    }}private:    vector<T> _array;    Compare<T> com;};int main(){    int a[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };    Heap<int,Less<int>> b(a, sizeof(a)/sizeof(int));    b.Pop();    b.Push(1);    system("pause");    return 0;}

堆的一些简单应用:

设计优先级队列:

template<typename T, typename Compare = Greater<T>>class PriorityQueue{public:    PriorityQueue(T* a, size_t size)        :_h(a, size)    {}    void Push(const T& d)    {        _h.Push(d);    }    void Pop()    {        _h.pop();    }    size_t Size()    {        return _h.Size();    }    const T& Top()    {        return _h.Top();    }    bool empty()    {        return _h.empty();    }protected:    Heap<T, Compare> _h;};
原创粉丝点击