有二叉树可以引申出堆,堆分为大堆和小堆,并可以在数据量很大的情况下以相当快的效率使用堆排序来计算出最大或者最小的前k个数
#include<iostream>
#include<stdlib.h>
#include<vector>
#include<assert.h>
using namespace std;
template<class T>
struct Less
{
bool operator()(const T& left, const T& right)const
{
return left < right;
}
};
template<class T>
struct Great
{
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)
{
//开空间 不把size增容 且对于空间不初始化
_a.reserve(n);
for (size_t i = 0; i < n; ++i)
{
//把数组里的数字放在vector里
_a.push_back(a[i]);
}
//建堆 构建成一个大堆 从最后一个非叶结点的父亲开始
for (int i = (_a.size() - 2) / 2; i >= 0; --i)
{
//向下调整算法 调成大堆
AdjustDown(i);
}
}
//即插入数据 从最后一个插入之后进行比较
void Push(const T& x)
{
_a.push_back(x);
AdjustUp(_a.size()-1);
}
void Pop()
{
//把最后一个节点和第一个节点进行交换 然后再进行向下调整
swap(_a[0], _a[_a.size() - 1]);
_a.pop_back();
AdjustDown(0);
}
//向下调整法 条件: 左右子树必须是大堆
void AdjustDown(int root)
{
Compare com;
int parent = root;
int child = (parent * 2) + 1;
//停止的条件是孩子存在 即继续的条件是孩子存在
while (child<_a.size())
{
//找左孩子右孩子大的那个 假设右孩子大于左孩子 右孩子可能不存在 需要一个判断
//if (child+1<_a.size()&&_a[child+1] > _a[child])
if (child + 1<_a.size() && com(_a[child + 1] , _a[child]))
{
child++;
}
//走到这 就指向大的那个孩子 如果孩子大于父亲节点 说明不满足大堆 即交换 交换完之后父亲和孩子节点要交换
//if (_a[child]> _a[parent])
if (com(_a[child], _a[parent]))
{
swap(_a[parent], _a[child]);
parent = child;
child = parent * 2 + 1;
}
//如果父亲节点大于大的那个孩子 说明父亲节点已经是最大的 不需要交换 即退出循环
else
{
break;
}
}
}
//向上调整法从最后一个节点开始 向上调整
void AdjustUp(int child)
{
//定义一个类型的对象
Compare com;
int parent = (child - 1) / 2;
while (child > 0)
{
//if (_a[child] >_a[parent])
if (com(_a[child], _a[parent]))
{
swap(_a[parent], _a[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
bool Empty()
{
return _a.empty();
}
size_t Size()
{
return _a.size();
}
T& Top()
{
//判断size是否大于0 如果不大于0则没有数据 取不了堆顶
assert(_a.size() > 0);
return _a[0];
}
//判断是否是堆 如果是大堆 父亲节点大于孩子节点
bool IsHeap()
{
//可以用递归实现
//return _IsHeap();
//遍历 判断父亲节点是否大于孩子节点
for (size_t i = 0; i < (_a.size() - 2) / 2; ++i)
{
if (_a[i] < _a[i * 2 + 1] || ((i*2+2)<_a.size()&&_a[i] < _a[i * 2 + 2]))
return false;
}
return true;
}
//建立小堆
void AdjustDown(int* heap, size_t k, size_t root)
{
assert(heap);
int parent = root;
int child = parent * 2 + 1;
while (child < k)
{
if (heap[child + 1] < heap[child])
++child;
if (heap[parent]>heap[child])
{
swap(heap[parent], heap[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//N个数,求最大的前k个//或者最小的前K个
void GetTopK(int* a,size_t n,size_t k)
{
assert(a != NULL);
int* heap = new int[k];
//建堆
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 (size_t 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] << endl;
}
delete[] heap;
}
protected:
vector<T> _a;
};
//<升序
//>降序
void testHeap()
{
int a[] = { 10, 24, 23, 27, 18, 16, 17 };
Heap<int> h(a,sizeof(a)/sizeof(a[7]));
Heap<int, Less<int>> h1(a, sizeof(a) / sizeof(a[7]));
h.Push(15);
h.Push(25);
h.Pop();
//h.GetTopK(a, sizeof(a) / sizeof(a[0]), 2);
//cout << h.Size() << endl;
cout << h.IsHeap() << endl;
}
void HeapSort(int* a,size_t n)
{
assert(a);
//建堆
for (int i = (n-2)/2; i >=0; --i)
{
AdjustDown(a, n,i);
}
//选择
int end = n - 1;
while (end > 0)
{
swap(a[0], a[end]);
AdjustDown(a, end, 0);
end--;
}
}
void testSort()
{
int a[] = { 10, 24, 23, 27, 18, 16, 17 };
HeapSort(a, sizeof(a) / sizeof(a[0]));
for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
{
cout << a[i] << " ";
}
cout << endl;
}