堆知识总结

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

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

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

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

思路:我们知道堆均是完全二叉树,因此一个父节点的子节点即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;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值