堆是一种完全二叉树,堆结构的二叉树存储方式分为大堆和小堆。
大堆:每个父节点都大于孩子节点
小堆:每个父节点都小于孩子节点
创建一个大堆:向下调整算法
思路:我们知道堆均是完全二叉树,因此一个父节点的子节点即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;
};