堆相关概念:
- 是一颗完全二叉树。
- 对任意结点满足若所有结点小于它的孩子(若有孩子的话),成为小根堆,若所有结点大于它的孩子,成为大根堆(若有孩子的话)。
- 堆顶元素一定是最大的或最小的。
- 每条路径一定是升序或者降序。
相关操作:
typedef int DataType;
typedef int (*PFUNC)(DataType left, DataType right);
typedef struct Heap
{
DataType* array;
int capacity;
int size;
PFUNC pCompare;
}Heap;
void Swap(DataType* left, DataType* right)
{
DataType temp = *left;
*left = *right;
*right = temp;
}
int Max(DataType left, DataType right)
{
return left > right;
}
int Min(DataType left, DataType right)
{
return left < right;
}
//向下调整
void AdjustDown(Heap* hp, int parent)
{
// 默认让child标记parent的左孩子,因为parent可能有左没有右
int child = parent*2+1;
int size = hp->size;
while (child < size)
{
// 先找parent较小的孩子
if (child + 1 < size && hp->pCompare(hp->array[child + 1], hp->array[child]))
child += 1;
// 检测parent是否满足堆的特性
if (hp->pCompare(hp->array[child], hp->array[parent]))
{
Swap(&hp->array[parent], &hp->array[child]);
// 大的元素往下走,可能会导致子树不满足堆的特性
parent = child;
child = parent * 2 + 1;
}
else
{
// parent已经满足堆的特性,不需要往下继续调整了
return;
}
}
}
//向上调整
void AdjustUp(Heap* hp, int child)
{
int parent = (child - 1) / 2;
while (child)
{
if (hp->pCompare(hp->array[child], hp->array[parent]))
{
Swap(&hp->array[child], &hp->array[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
return;
}
}
}
//容量检查
void HeapCheckCapacity(Heap* hp)
{
assert(hp);
if (hp->size == hp->capacity)
{
int newCapacity = hp->capacity * 2;
DataType* tmp = (DataType*)malloc(sizeof(DataType)*newCapacity);
if (NULL == tmp)
{
assert(0);
return;
}
memcpy(tmp, hp->array, sizeof(DataType)*hp->size);
free(hp->array);
hp->array = tmp;
hp->capacity;
}
}
// 创建堆的时候需要用到向下调整
void HeapCreate(Heap* hp, DataType array[], int size, PFUNC pCompare)
{
assert(hp);
// 1. 将hp指向的堆结构体初始化好
hp->array = (DataType*)malloc(sizeof(DataType)* size);
if (NULL == hp->array)
{
assert(0);
return;
}
hp->capacity = size;
// 2. 将数组中的元素往堆中存放
memcpy(hp->array, array, size*sizeof(DataType));
hp->size = size;
hp->pCompare = pCompare;
// 3. 需要对hp->array中的元素进行调整
// BubbleSort(hp->array, size);
// 调试技巧:不想看整个循环过程,指向看root=0时向下调整过程--->下条件断点
for (int root = (size - 2) / 2; root >= 0; root--)
{
AdjustDown(hp, root); // O(N/2*(logN))===>O(NlogN) 错误
}
}
// 插入
void HeapPush(Heap* hp, DataType data)
{
// 扩容
HeapCheckCapacity(hp);
hp->array[hp->size] = data;
hp->size++;
AdjustUp(hp, hp->size-1);
}
// 删除
void HeapErase(Heap* hp)
{
if (HeapEmpty(hp))
return;
// 1. 先将堆顶元素与堆中最后一个元素进行交换
Swap(&hp->array[0], &hp->array[hp->size - 1]);
// 2. 再将堆中有效元素个数减少一个
hp->size--;
// 3. 最后将堆顶元素往下调整
AdjustDown(hp, 0);
}
// 获取堆顶元素
DataType HeapTop(Heap* hp)
{
assert(hp);
return hp->array[0];
}
//获取堆中元素个数
int HeapSize(Heap* hp)
{
assert(hp);
return hp->size;
}
//判断是否为空
int HeapEmpty(Heap* hp)
{
assert(hp);
return 0 == hp->size;
}
//堆的销毁
void HeapDestroy(Heap* hp)
{
assert(hp);
if (hp->array)
{
free(hp->array);
hp->array = NULL;
hp->capacity = 0;
hp->size = 0;
}
}
//测试
void TestHeap()
{
int array[] = { 49, 27, 37, 65, 28, 34, 25, 15, 18, 19 };
Heap hp;
HeapCreate(&hp, array, sizeof(array) / sizeof(array[0]), Max);
printf("top = %d\n", HeapTop(&hp));
printf("size = %d\n", HeapSize(&hp));
HeapPush(&hp, 10);
printf("top = %d\n", HeapTop(&hp));
printf("size = %d\n", HeapSize(&hp));
HeapErase(&hp);
printf("top = %d\n", HeapTop(&hp));
printf("size = %d\n", HeapSize(&hp));
HeapDestroy(&hp);
}
TOP-K问题
将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。
void PrintTopK(int* a, int n, int k)
{
Heap hp;
//建立含有K个元素的堆
HeapCreate(&hp, a, k,Max);
for (size_t i = k; i < n; ++i) // N
{
//每次和堆顶元素比较,小于堆顶元素,则删除堆顶元素,插入新的元素
if (a[i] < HeapTop(&hp)) // LogK
{
HeapErase(&hp);
HeapPush(&hp, a[i]);
}
}
for (int i = 0; i < k; ++i) {
printf("%d ", HeapTop(&hp));
HeapErase(&hp);
}
}
int main()
{
int n = 10000;
int* a = (int*)malloc(sizeof(int) * n);
srand(time(0));
//随机生成10000个数存入数组,保证元素都小于1000000
for (size_t i = 0; i < n; ++i)
{
a[i] = rand() % 1000000;
}
//确定10个最大的数
a[5] = 1000000 + 1;
a[1231] = 1000000 + 2;
a[531] = 1000000 + 3;
a[5121] = 1000000 + 4;
a[115] = 1000000 + 5;
a[2335] = 1000000 + 6;
a[9999] = 1000000 + 7;
a[76] = 1000000 + 8;
a[423] = 1000000 + 9;
a[3144] = 1000000 + 10;
PrintTopK( a, n, 10);
}
堆排序
void PrintArray(int array[], int size)
{
for (int i = 0; i < size; ++i)
{
printf("%d ", array[i]);
}
printf("\n");
}
// 向下调整
void HeapAdjust(int array[], int size, int parent)
{
int child = parent * 2 + 1;
while (child < size)
{
if (child + 1 < size && array[child + 1] < array[child])
child += 1;
if (array[child] < array[parent])
{
int temp = array[child];
array[child] = array[parent];
array[parent] = temp;
parent = child;
child = parent * 2 + 1;
}
else
{
return;
}
}
}
// 利用堆的思想来进行排序
void HeapSort(int array[], int size)
{
// 1. 建堆----大堆?小堆?
// 排升序---->大堆 排降序--->小堆
for (int root = (size - 2) / 2; root >= 0; root--)
{
HeapAdjust(array, size, root);
}
// 2. 利用堆删除的思想来排序
int end = size - 1;
while (end)
{
int temp = array[0];
array[0] = array[end];
array[end] = temp;
HeapAdjust(array, end, 0);
end--;
}
}
int main()
{
int array[] = { 3, 1, 8, 6, 0, 2, 7, 9, 4, 5 };
PrintArray(array, sizeof(array) / sizeof(array[0]));
HeapSort(array, sizeof(array) / sizeof(array[0]));
PrintArray(array, sizeof(array) / sizeof(array[0]));
}