文章目录
堆的概念
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树。
堆的结构
typedef int HPDateType;
typedef struct Heap
{
HPDateType* a;
int size;
int capacity;
}HP;
堆的初始化
void HeapInit(HP* hp)
{
hp->a = NULL;
hp->capacity = 0;
hp->size = 0;
}
堆的向上和向下调整
向上调整
若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
若想将其调整为大堆,那么根结点的左右子树必须都为大堆。
void AdjustUp(HP* hp, int n, int child)
{
assert(hp);
int parent = (child - 1) / 2;
while (child > 0)
{
if (hp->a[child] < hp->a[parent])
{
swap(&hp->a[child], &hp->a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
向下调整
若想将其调整为小堆,那么根结点的左右子树必须都为小堆。
若想将其调整为大堆,那么根结点的左右子树必须都为大堆。
void AdjustDown(HP* hp, int n, int parent)
{
int child = parent * 2 + 1;
while (child<n)
{
if (child + 1 < n && hp->a[child + 1] < hp->a[child])
{
child++;
}
if (hp->a[child] < hp->a[parent])
{
swap(&hp->a[parent], &hp->a[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
堆的插入、删除
插入
先插入一个9到数组的尾上,再进行向上调整算法,直到满足堆。
void HeapPush(HP* hp, HPDateType x)
{
assert(hp);
if (hp->capacity == hp->size)
{
int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
HPDateType* php = realloc(hp->a, sizeof(HPDateType) * newcapacity);
if (php == NULL)
{
printf("realloc fail");
exit(-1);
}
hp->capacity = newcapacity;
hp->a = php;
}
hp->a[hp->size] = x;
hp->size++;
AdjustUp(hp, hp->size, hp->size - 1);
}
删除
删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
void HeapPop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
swap(&hp->a[0], &hp->a[hp->size-1]);
hp->size--;
AdjustDown(hp, hp->size, 0);
}
堆的打印、判空
打印
void HeapPrint(HP* hp)
{
int i = 0;
for (i = 0; i < hp->size; i++)
{
printf("%d ", hp->a[i]);
}
printf("\n");
}
判空
bool HeapEmpty(HP* hp)
{
assert(hp);
return hp->size == 0;
}
堆的数据个数和堆顶数据
堆顶数据
HPDateType HeapTop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
return hp->a[0];
}
堆数据个数
int HeapSize(HP* hp)
{
assert(hp);
return hp->size;
}
堆的销毁
void HeapDestroy(HP* hp)
{
free(hp->a);
hp->a = NULL;
hp->capacity = 0;
hp->size = 0;
}
TopK问题
给一个无序的数组,长度为N, 请输出最小 (或最大)的K个数。
思路
1.先把前k个数插入进小堆
2.再把后n-k个数和堆顶比较如果比堆顶大,替换堆顶并向下调整
void TopK(HPDateType* a, int n, int k)
{
HP hp;
HeapInit(&hp);
for (int i = 0; i < k; i++)
{
HeapPush(&hp, a[i]);
}
for (int i = k; i < n; i++)
{
if (HeapTop(&hp) < a[i])
{
HeapPop(&hp);
HeapPush(&hp, a[i]);
}
}
HeapPrint(&hp);
HeapDestroy(&hp);
}
完整代码
Heap.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <time.h>
typedef int HPDateType;
typedef struct Heap
{
HPDateType* a;
int size;
int capacity;
}HP;
void HeapInit(HP* hp);
void HeapDestroy(HP* hp);
void HeapPush(HP* hp, HPDateType x);
void HeapPrint(HP* hp);
void HeapPop(HP* hp);
HPDateType HeapTop(HP* hp);
int HeapSize(HP* hp);
void AdjustUp(HP* hp, int n, int child);
void AdjustDown(HP* hp, int n, int child);
bool HeapEmpty(HP* hp);
void swap(HPDateType* hp1, HPDateType* hp2);
void TopK(HPDateType* a, int n, int k);
Heap.c
#include "Heap.h"
void swap(HPDateType* hp1, HPDateType* hp2)
{
HPDateType temp = *hp1;
*hp1 = *hp2;
*hp2 = temp;
}
void HeapInit(HP* hp)
{
hp->a = NULL;
hp->capacity = 0;
hp->size = 0;
}
void HeapDestroy(HP* hp)
{
free(hp->a);
hp->a = NULL;
hp->capacity = 0;
hp->size = 0;
}
void AdjustUp(HP* hp, int n, int child)
{
assert(hp);
int parent = (child - 1) / 2;
while (child > 0)
{
if (hp->a[child] < hp->a[parent])
{
swap(&hp->a[child], &hp->a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
void HeapPush(HP* hp, HPDateType x)
{
assert(hp);
if (hp->capacity == hp->size)
{
int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
HPDateType* php = realloc(hp->a, sizeof(HPDateType) * newcapacity);
if (php == NULL)
{
printf("realloc fail");
exit(-1);
}
hp->capacity = newcapacity;
hp->a = php;
}
hp->a[hp->size] = x;
hp->size++;
AdjustUp(hp, hp->size, hp->size - 1);
}
void HeapPrint(HP* hp)
{
int i = 0;
for (i = 0; i < hp->size; i++)
{
printf("%d ", hp->a[i]);
}
printf("\n");
}
bool HeapEmpty(HP* hp)
{
assert(hp);
return hp->size == 0;
}
HPDateType HeapTop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
return hp->a[0];
}
int HeapSize(HP* hp)
{
assert(hp);
//assert(!HeapEmpty(hp));
return hp->size;
}
void AdjustDown(HP* hp, int n, int parent)
{
int child = parent * 2 + 1;
while (child<n)
{
if (child + 1 < n && hp->a[child + 1] < hp->a[child])
{
child++;
}
if (hp->a[child] < hp->a[parent])
{
swap(&hp->a[parent], &hp->a[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
void HeapPop(HP* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
swap(&hp->a[0], &hp->a[hp->size-1]);
hp->size--;
AdjustDown(hp, hp->size, 0);
}
void TopK(HPDateType* a, int n, int k)
{
HP hp;
HeapInit(&hp);
for (int i = 0; i < k; i++)
{
HeapPush(&hp, a[i]);
}
for (int i = k; i < n; i++)
{
if (HeapTop(&hp) < a[i])
{
HeapPop(&hp);
HeapPush(&hp, a[i]);
}
}
HeapPrint(&hp);
HeapDestroy(&hp);
}
Test.c
#include "Heap.h"
void TestHeap()
{
HPDateType a[] = { 70,56,30,25,15,10 };
int len = sizeof(a) / sizeof(a[0]);
HP hp;
HeapInit(&hp);
for (int i = 0; i < len; i++)
{
HeapPush(&hp, a[i]);
}
HeapPrint(&hp);
HeapPush(&hp, 75);
HeapPrint(&hp);
HeapPop(&hp);
HeapPrint(&hp);
HPDateType ret = HeapTop(&hp);
printf("%d\n", ret);
HeapDestroy(&hp);
}
void TopKPrint(int* a, int k)
{
for (int i = 0; i < k; i++)
{
printf("%d ", a[i]);
}
}
void TestTopk()
{
int n = 10000;
HPDateType* a = (HPDateType*)malloc(sizeof(HPDateType) * n);
if (a == NULL)
{
printf("malloc fail");
exit(-1);
}
srand((unsigned int)time(NULL));
for (int i = 0; i < n; i++)
{
a[i] = rand() % 1000000;
}
a[5] = 1000000 + 1;
a[55] = 1000000 + 2;
a[555] = 1000000 + 3;
a[444] = 1000000 + 4;
a[4444] = 1000000 + 5;
a[333] = 1000000 + 6;
a[3333] = 1000000 + 7;
a[5555] = 1000000 + 8;
a[111] = 1000000 + 9;
a[2222] = 1000000 + 10;
TopK(a, n, 10);
}
int main()
{
//TestHeap();
TestTopk();
return 0;
}