堆的概念:
如果有一个关键码的集合K = {k0, k1, k2 ,·······, kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: ki <= k2*i + 1 且ki <= k2*i + 2 (ki >= k2*i + 1 且ki >= k2*i + 2) i= 0, 1, 2, 3······,则称这个推为最小堆(或最大堆)。(堆的底层其实就是一个数组,而后面所说的左右孩子其实就是相对与图中,ki 这个节点的左右孩子其实就是下标为 ki*2+1,和ki*2+2的数)
最小堆: 任一节点的关键码都小于或等于他孩子节点的关键码,位于堆顶节点的关键码最小。从根节点到每个节点的路径上数组元素组成的序列都是递增的。
最大堆: 任一节点的关键码都大于或等于他孩子节点的关键码,位于堆顶节点的关键码最大。从根节点到每个节点的路径上数组元素组成的序列都是递减的。
假设有一组数组 int array[] = { 53, 117, 78, 9, 45, 65, 87, 23 };先按照二叉树的方式插入每一个节点插入后:
如果将array数组中的数按照最小堆排序则:
从int idx= (sizeof(array)/sizeof(array[0]) - 1)>>1的元素处开始调整,然以后依次--idx处的节点调整,一直到堆顶。此时idx的值为3则从标号为3的节点开始调整。
从上面的示意图中可以看出每次检测的时候都是从他的左右孩子中取一个较小值和其进行比较如果满足小堆的性质则进行下一个节点的检测,如果不满足则交换他们的值,然后继续检测他的孩子节点是否还满足小堆的性质一直到这个子树的叶子节点,完成之后才进行下一个节点的检测。最后将节点的个数加一。
节点的插入
每次插入一个节点都是在叶子节点出插入,然后进行向上调整(向上调整就是和刚才小堆的调整一样从新插入的节点的双亲节点处开始检测,如果满足则直接退出,如果不满足则继续进行向上调整,直到根节点)
节点的删除
节点的删除其实就是删除根节点(因为根节点才是最小或者最大的节点删除其他节点没有意义),而删除根节点不好删除,此时我们就需要一种相对与替换法的方法,就是将根节点和最后一个叶子节点交换然后让节点的个数减1。然后进行向下调整(此时的向下调整和向上调整刚好相反,从根节点开始和他的左右孩子中较小的一个进行比较,取其中较小的一个和他进行交换,然后继续向下调整(而这里的调整我认为只调整和他交换的那个节点的子树因为另外的那个节点的子树并没有调整所以还是满足的堆的性质的),直到叶子节点。)
#include "iostream"
using namespace std;
#include <vector>
#include <assert.h>
template<class T>
struct Less
{
bool operator()(const T& left, const T& right)
{
return left < right;
}
};
template<class T>
struct Greater
{
bool operator()(const T& left, const T& right)
{
return left > right;
}
};
// 小堆
template<class T, class Compare = Less<T>>
class Heap
{
public:
Heap()
{}
Heap(const T array[], int size)
{
_heap.reserve(size);
for (int i = 0; i < size; ++i)
_heap.push_back(array[i]);
int index = (_heap.size() - 1)>>1;
for (; 0 <= index; --index)
_AdjustDown(index);
}
void Insert(const T& data)
{
_heap.push_back(data);
int index = (_heap.size() - 1) >> 1;
//堆的插入每次都在已经建成的而最小堆的后面插入,但插入之后,有可能破坏了堆的结构,这时就需要对堆自下而上的对其调整。
_AdjustUp(index);
}
void Remove()
{
assert(!_heap.empty());
std::swap(_heap[0], _heap[_heap.size() - 1]);
_heap.pop_back();
//堆的删除是:从堆中删除堆顶元素。移除堆顶元素之后,用堆的最后一个节点填补取 走的堆顶元素,并将堆的实际元素个数减1。
//但用最后一个元素取代堆顶元素之后有可能破坏堆,因此需要将对自顶向下调整,使其满足最大或最小堆。
_AdjustDown(0);
}
size_t Size()const
{
return _heap.size();
}
bool Empty()const
{
return _heap.empty();
}
const T& Top()const
{
return _heap[0];
}
protected:
void _AdjustDown(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < _heap.size())
{
Compare com;
if ((child + 1) < _heap.size() && com(_heap[child + 1], _heap[child]))
child += 1;
if (com(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
parent = child;
child = child * 2 +1;
}
else
return;
}
}
void _AdjustUp(size_t child)
{
size_t parent = (child - 1)>>1;
while (child != 0)
{
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[child], _heap[parent]);
child = parent;
parent = (child - 1) >> 1;;
}
else
return;
}
}
private:
std::vector<T> _heap;
};
template<class T, class Compare = Less<T>>
class PriorityQueue
{
public:
PriorityQueue()
{}
void Push(const T& data)
{
_hp.Insert(data);
}
void Pop()
{
_hp.Remove();
}
const T& Top()const
{
_hp[0];
}
size_t Size()const
{
_hp.Size();
}
bool Empty()const
{
_hp.Empty();
}
protected:
Heap<T, Compare> _hp;
};
int main()
{
int array[] = { 53, 117, 78, 9, 45, 65, 87, 23 };
//PriorityQueue<int> Pq;
//Pq.Push(1);
Heap<int,Greater<int>> hp(array,sizeof(array)/sizeof(array[0]));//创建大堆
//hp.Insert(1);
//hp.Remove();
return 0;
}