1.可以用做外排序。
比如查找N个数据(假设数据量内存放不下)中最大的前K个,
取这N个数据中的前K个建一个小堆,
因为是小堆,所以,堆顶数据必然是最小的。
此时,读取剩余数据,只要是大于堆顶数据,就交换堆顶数据和读到的数据。
然后调整堆,让堆顶继续是最小数据。
(永远保证堆里面保存着读之前所有数据中最大的前K个)
一直读取,直到所有数据读结束。 此时堆里就保存着这N个数据中最大的前K个。
如果是最小的前K个,同理,建大堆。
2.堆也可以直接对一组数据排序。
如果是排成升序,建大堆。
大堆保证了堆顶数据是最大的,且每个节点都是大堆。
堆顶既然已经是最大的数据,堆顶的数据已经有序,所以把堆顶的数据放在数组最后面(因为是升序)
再用原堆底的数据来填补堆顶。(即交换 堆顶和堆低)。
此时再对除堆低以外的数据继续建大堆,然后交换堆低和堆顶,直到只剩最后一个数据,
此时数组有序。
同理,排降序,则建小堆。
1.最大前K个数据如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <windows.h>
#include <vector>
#include <assert.h>
#include <utility>
using namespace std;
template<class T>
class Less
{
public:
bool operator()(const T& left, const T& right) const
{
return left < right;
}
};
template<class T>
class Great
{
public:
bool operator()(const T& left, const T& right) const
{
return left > right;
}
};
template<class T, class Compare = Great<T>>
class Heap
{
public:
Heap()
{}
Heap(T* a, size_t n)
{
_a.reserve(n);
for (size_t i = 0; i < n; i++)
{
_a.push_back(a[i]);
}
//建堆
for (int i = (_a.size() - 2) / 2; i >= 0; --i)
{
AdjustDown(i);
}
}
void AdjustDown(int root) //向下调整
{
Compare com;
int parent = root;
int child = parent * 2 + 1;
while (child < _a.size())
{
if (child + 1 < _a.size() && com(_a[child + 1], _a[child])) //选出 最大的一个孩子
{
++child;
}
if (com(_a[child], _a[parent])) //让父亲是最大的
{
swap(_a[child], _a[parent]);
parent = child;
child = child * 2 + 1;
}
else
{
break;
}
}
}
void AdjustUp(int child) //向上调整
{
Compare com;
int parent = (child - 1) / 2;
while ()
{
if (com(_a[parent], _a[child]))
{
swap(_a[parent], _a[child]);
child = patent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
protected:
vector<T> _a;
};
void AdjustDown(int* heap, int n, int root)
{
assert(heap);
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
if (child + 1 < n && heap[child + 1] < heap[child])
{
++child;
}
if (heap[child] < heap[parent])
{
swap(heap[child], heap[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void TopK()
{
const size_t N = 10000;
const size_t K = 10;
int a[N] = { 0 };
for (size_t i = 0; i < N; i++)
{
a[i] = rand() % N;
}
a[0] = N + 100;
a[100] = N + 101;
a[2] = N + 102;
a[50] = N + 105;
a[1000] = N + 100;
a[1005] = N + 1550;
a[888] = N + 130;
a[9998] = N + 100;
a[9999] = N + 100;
a[9444] = N + 100;
int heap[K] = { 0 };
for (size_t i = 0; i < K; i++)
{
heap[i] = a[i];
}
// 建小堆 堆顶为最小数据
for (int i = (K - 2) / 2; i >= 0; i--)
{
AdjustDown(heap, K, i);
}
//大于堆顶的数据,放入堆 调整一次
for (int i = K; i < N; i++)
{
if (a[i] > heap[0])
{
heap[0] = a[i];
AdjustDown(heap, K, 0);
}
}
for (size_t i = 0; i < K; i++)
{
cout << heap[i] << " ";
}
cout << endl;
}
int main()
{
int a[] = { 10,11, 13, 12, 16, 18, 15, 17, 14, 19 };
Heap<int>hp(a, sizeof(a) / sizeof(a[0]));
TopK();
system("pause");
return 0;
}
2.最大前K个
void AdjustDown(int a[], int root, int len) //向下调整 //
{
assert(a);
int parent = root;
int child = parent * 2 + 1;
while (child < len) //检查孩子节点是否存在
{
if (child + 1 < len && 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(int a[], int len) //升序,建大堆
{
assert(a);
for (int i = (len - 1) / 2; i >= 0; i--)
{
AdjustDown(a, i, len); //建大堆,必须保证根节点(堆顶)是最大的。
}
int end = len;
while (end > 0)
{
swap(a[end], a[0]); //用最后一个节点替换根节点。
AdjustDown(a, 0, end);
end--; //此时最后一个节点已经是最大节点(已经有序),去掉最后一个节点。
}
}