一、堆的定义
如果有一个关键码的集合K={k0,k1,k2,,,,,,kn-1},把它所有元素按完全二叉树的层序顺序存储在一个一维数组中,并满足任意结点的值小于(大于)等于后代结点的值,则称为小堆(大堆)。
#pragma once
typedef int HeapDataType;
typedef struct Heap
{
HeapDataType * _arr;
size_t _size;
size_t _capacity;
}Heap;
//初始化堆
void InitHeap(Heap* hp, HeapDataType* arr, size_t length)
{
assert(hp);
hp->_arr = (HeapDataType*)malloc(sizeof(HeapDataType)*length);
assert(hp->_arr);
hp->_size = length;
hp->_capacity = length;
for (size_t i = 0; i < length; i++)
{
hp->_arr[i] = arr[i];
}
}
void Swap(HeapDataType *hp1, HeapDataType *hp2)
{
HeapDataType temp = *hp1;
*hp1 = *hp2;
*hp2 = temp;
}
//向下调整堆
void siftDown(Heap* hp, size_t root)
{
size_t parent = root;
size_t child = parent * 2 + 1;
while (child < hp->_size)
{
if (child + 1 < hp->_size&&hp->_arr[child + 1] < hp->_arr[child])
{
child++;
}
if (hp->_arr[child] < hp->_arr[parent])
{
Swap(&hp->_arr[child], &hp->_arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//向上调整堆
void siftUp(Heap* hp, size_t child)
{
assert(hp);
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (hp->_arr[child] < hp->_arr[parent])
{
Swap(&hp->_arr[child], &hp->_arr[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//初建堆,指定大堆或小堆
void CreateHeap(Heap* hp)
{
assert(hp);
for (int i = (hp->_size) / 2 - 1; i >= 0; i--)
{
siftDown(hp, i);
}
}
//扩容
void checkCapacity(Heap* hp)
{
assert(hp);
if (hp->_size == hp->_capacity)
{
hp->_capacity *= 2;
hp->_arr = (HeapDataType*)realloc(hp->_arr, sizeof(HeapDataType)*hp->_capacity);
assert(hp->_arr);
}
}
//插入
void HeapPush(Heap* hp, HeapDataType x)
{
assert(hp);
checkCapacity(hp);
hp->_arr[hp->_size++] = x;
siftUp(hp, hp->_size - 1);
}
//删除
void HeapPop(Heap* hp)
{
if (hp->_size > 0)
{
Swap(&hp->_arr[0], &hp->_arr[hp->_size - 1]);
hp->_size--;
siftDown(hp, 0);
}
}
//获取堆的size
size_t HeapSize(Heap* hp)
{
assert(hp);
return hp->_size;
}
//是否为空
size_t HeapEmpty(Heap* hp)
{
assert(hp);
return hp->_size;
}
//返回堆顶元素
HeapDataType HeapTop(Heap* hp)
{
assert(hp&&hp->_size > 0);
return hp->_arr[0];
}
void printHeap(Heap* hp)
{
assert(hp);
for (size_t i = 0; i < hp->_size; i++)
{
printf("%d ", hp->_arr[i]);
}
cout << endl;
}
//堆排序
void HeapSort(int *arr, int length)
{
Heap hp;
InitHeap(&hp, arr, length);
CreateHeap(&hp);
while (HeapSize(&hp) > 1)
{
Swap(&hp._arr[0], &hp._arr[hp._size - 1]);
hp._size--;
siftDown(&hp, 0);
}
hp._size = length;
printHeap(&hp);
}
//大数据中最大的前几个数
void HeapTopK(int *arr, int length, size_t k)
{
Heap hp;
InitHeap(&hp, arr, k);
for (size_t i = k; i < length; i++)
{
if (arr[i] > HeapTop(&hp))
{
hp._arr[0] = arr[i];
siftDown(&hp, 0);
}
}
printHeap(&hp);
}
void TestHeap()
{
HeapDataType arr[] = { 53,17,78,9,45,65,87,23,31 };
size_t length = sizeof(arr) / sizeof(arr[0]);
Heap hp;
InitHeap(&hp, arr, length);
CreateHeap(&hp);
printHeap(&hp);
HeapPush(&hp, 100);
printHeap(&hp);
HeapPush(&hp, 40);
printHeap(&hp);
HeapTopK(arr, length, 3);
HeapSort(arr, length);
}
注意:
1、要找数组前k个最大值要建小堆,向下调整,最大的前K个一定会换到堆顶
2、排序,升序建大堆,把堆顶元素(最大的)找到,然后和堆尾交换,然后在除过队尾 ,给其他的元素重新向下调整。
降序建小堆,把堆顶元素(最小的)找到,然后和堆尾交换,然后在除过堆尾 ,给其他的元素重新向下调整。