堆的应用:topk问题以及堆排序
一. topk问题
topk问题就是给上一大堆数据,然后找出其中最大的k个数据
(1).使用这些数据的前k个数据建一个堆(求最大k个数需要建小堆,反之则用大堆)
(2).读取下一数据,与堆顶数据作比较,满足条件则用这个数据将堆顶数据替换掉(求最大k个数时读取到的数据应大于堆顶数据才满足替换条件,反之则需要小于堆顶数据),替换之后重新调整堆为大堆或者小堆,不满足条件的数据直接掠过,无需进堆
(3).继续读取数据重复步骤(2)
如下是求这个数组中的最大三个数的过程
void TopK(const vector<int>& v, int K)
{
Heap<int> MinHeap;//建小堆
for (int i = 0; i < v.size(); i++)
{
if (MinHeap.Size() < K)
{
MinHeap.Push(v[i]);
}
else
{
if (v[i]>MinHeap.Root())//与堆顶数据比较
{
size_t root = (K - 2) / 2;
MinHeap.Root() = v[i];//替换
MinHeap._AdjDown(root);//调整堆
}
}
}
MinHeap._Dispaly(K);//打印输出
}
在数据量特别大时,数据存在磁盘文件之中,用堆求取topk问题原理与此是相同的,只是从内存中读取数据变为从硬盘读取。
二. 堆排序
用堆进行排序,所有的数据都需要放进同一个堆中,数据量不能特别大
(1).用所给数据建小堆(用于降序)(要升序则建大堆)
(2).将堆顶数据与堆中最后一个数据进行交换,这时最大或者最小数据就是堆中最后一个数据,堆的容量减一
(3).将剩下的数据重新调整为小堆或者大堆
(4).重复步骤(2)和(3)直至所有数据排完
void _AdjDown(size_t root,size_t size)
{
size_t parent = root;
size_t child = parent * 2 + 1;
while (child < size)
{
if ((child + 1 < size) && (_a[child] > _a[child + 1]))
++child;
if (_a[parent] > _a[child])
{
swap(_a[child], _a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void _HeapSort()
{
size_t size = _a.size();
while (size > 0)
{
swap(_a[0], _a[size-1]);
size--;
_AdjDown(0,size);
}
}
void HeapSort(int* a, size_t n)
{
Heap<int> MinHeap;
for (int i = 0; i < n; i++)
{
MinHeap.Push(a[i]);
}
MinHeap._HeapSort();
MinHeap._Dispaly(n);
}