- 堆
堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
a[i]<=a[2i+1]&&a[i]<=a[2i+2]或者a[i]>=a[2i+1]&&a>=key[2i+2]
,即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。
大堆a[i]>=a[2i+1]&&a>=a[2i+2]
小堆 a[i]<=a[2i+1]&&a[i]<=a[2i+2]
由上述性质可知大堆的堆顶的关键字肯定是所有关键字中最大的,小堆的堆顶的关键字是所有关键字中最小的。 - 堆排序思想
parent=child*2+1;
child=(parent-1)/2;
利用大堆(小堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。
其基本思想为(大堆):
1)将初始待排序关键字序列(a1,a2….an)构建成大顶堆,此堆为初始的无序区;
2)将堆顶元素a[1]与最后一个元素a[n]交换,此时得到新的无序区(a1,a2,……an-1)和新的有序区(an),且满足R[1,2…n-1]<=R[n];
3)由于交换后新的堆顶a[1]可能违反堆的性质,因此需要对当前无序区(a1,a2,……an-1)调整为新堆,然后再次将a[1]与无序区最后一个元素交换,得到新的无序区(a1,a2….an-2)和新的有序区(an-1,an)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。 建堆
这里我们用STL的vector实现。
首先根据给的数组创建一个二叉树
然后再逐步进行向下调整算法,我们可以先用图理解:
就是从倒数第一个非叶子节点开始调整,将子节点中大的(大堆)与父节点进行比较,如果大于父节点就交换,这就是建堆。
另外这里我们可以用写个仿函数,这样我们就可以根据调用来决定创建大堆还是小堆。
仿函数实现代码:
template<class T>
struct Less
{
bool operator()(const T& s, const T& l)
{
return s < l;
}
};
template<class T>
struct Greater
{
bool operator()(const T& s, const T& l)
{
return s > l;
}
};
向下调整实现代码:
void _AdjustDown(size_t i)
{
Compare com;
size_t parent = i;
size_t child = 2 * parent + 1;
while (parent < _a.size())
{
if (child + 1 < _a.size() &&com (_a[child+1],_a[child]))
{
++child;
}
if (child < _a.size() && com(_a[child],_a[parent]))
{
swap(_a[child], _a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
4.插入
因为我们使用的vector进行模拟,所以直接使用vector自带push_back(),然后对堆进行调整。
向上调整就是从最后一个节点开始,与他的父节点进行比较。
然后根据大堆或者小堆进行调整。
void Push(const T& x)
{
_a.push_back(x);
_AdjustUp(_a.size()-1);
}
void _AdjustUp(size_t i)
{
assert(!_a.empty()); //为空访问越界了
Compare com;
size_t child = i;
while (child > 0)
{
size_t parent = (child - 1) >> 1;
if (com(_a[child],_a[parent]))
{
swap(_a[child], _a[parent]);
child = parent;
}
else
{
break;
}
}
}
5.删除
要删除的是堆顶元素,我们可以将最后一个叶子节点和堆顶元素交换,然后使用vector的popback,将原来的堆顶元素删除
然后重新向下调整堆。
void Pop()
{
assert(!_a.empty);
std::swap(arr[0], arr[_a.size() - 1]);//交换堆顶元素与堆底元素
_a.pop_back();
_AdjustDown(0);
}
void _AdjustUp(size_t i)
{
assert(!_a.empty()); //为空访问越界了
Compare com;
size_t child = i;
while (child > 0)
{
size_t parent = (child - 1) >> 1;
if (com(_a[child],_a[parent]))
{
swap(_a[child], _a[parent]);
child = parent;
}
else
{
break;
}
}
}
6.完整实现代码
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;
template<class T>
struct Less
{
bool operator()(const T& s, const T& l)
{
return s < l;
}
};
template<class T>
struct Greater
{
bool operator()(const T& s, const T& l)
{
return s > l;
}
};
template<class T=int,class Compare=Greater<T>>
class Heap
{
public:
Heap(T* arr, size_t size)
{
for (size_t i = 0; i < size; i++)
{
_a.push_back(arr[i]);
}
for (int j = (size - 2) / 2; j >= 0;j--)
{
_AdjustDown(j);
}
}
void Push(const T& x)
{
_a.push_back(x);
_AdjustUp(_a.size()-1);
}
void Pop()
{
assert(!_a.empty);
std::swap(arr[0], arr[_a.size() - 1]);//交换堆顶元素与堆底元素
_a.pop_back();
_AdjustDown(0);
}
T& Top()
{
return _a[0];
}
protected:
void _AdjustDown(size_t i)
{
assert(!_a.empty());
Compare com;
size_t parent = i;
size_t child = 2 * parent + 1;
while (parent < _a.size())
{
if (child + 1 < _a.size() &&com (_a[child+1],_a[child]))
{
++child;
}
if (child < _a.size() && com(_a[child],_a[parent]))
{
swap(_a[child], _a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void _AdjustUp(size_t i)
{
assert(!_a.empty()); //为空访问越界了
Compare com;
size_t child = i;
while (child > 0)
{
size_t parent = (child - 1) >> 1;
if (com(_a[child],_a[parent]))
{
swap(_a[child], _a[parent]);
child = parent;
}
else
{
break;
}
}
}
protected:
vector<T> _a;
};
void HeapTest()
{
int arr[] = { 16,7,3,20,17,8 };
Heap<int> a(arr, sizeof(arr) / sizeof(arr[0]));
a.Push(99);
a.Pop();
}