堆-Heap
- [1. 介绍](#1. 介绍)
- [2. 堆的实现](#2. 堆的实现)
- [3. 堆操作](#3. 堆操作)
- [4. HeapSort](#4. HeapSort)
- [5. Top-K](#5. Top-K)
1. 介绍
堆(heap) 是一种满足特定条件的完全二叉树。
- 小顶堆(min heap):任意节点的值 ≤ 其子节点的值。
- 大顶堆(max heap):任意节点的值 ≥ 其子节点的值
堆作为完全二叉树的一个特例,具有以下特性。
- 最底层节点靠左填充,其他层的节点都被填满。
- 将根节点称为"堆顶",将底层最靠右的节点称为"堆底"。
- 大顶堆,根节点值是最大的。
- 小顶堆,根节点值是最小的。
2. 堆的实现
完全二叉树非常适合用数组来表示。
使用数组表示二叉树时,元素代表节点值,索引代表节点在二叉树中的位置。
节点指针通过索引映射公式来实现:
给定索引 𝑖 ,其左子节点的索引为 2𝑖 + 1 ,右子节点的索引为 2𝑖 + 2 ,父节点的索引为(𝑖 - 1)/2(向下整除)。当索引越界时,表示空节点或节点不存在。
3. 堆操作
Initlilze
c
void HeapInit(HP* php) {
assert(php);
php->a = NULL;
php->size = 0;
php->capacity = 0;
}
Destory
c
void HeapDestory(HP* php) {
assert(php);
free(php->a);
php->a = NULL;
php->capacity = 0;
php->size = 0;
}
Swap
c
void Swap(HPDataType* p1, HPDataType* p2) {
HPDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
Push
c
void HeapPush(HP* php, HPDataType x) {
assert(php);
if (php->size == php->capacity) {
int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));
php->a = tmp;
php->capacity = newCapacity;
}
php->a[php->size] = x;
php->size++;
AdjustUp(php->a, php->size - 1);//向上排序
}
Pop


c
void HeapPop(HP* php) {
assert(php);
assert(!HeapEmpty(php));
Swap(&php->a[0], &php->a[php->size - 1]);
php->size--;
AdjustDown(php->a, php->size, 0);
}
Top
c
HPDataType HeapTop(HP* php) {
assert(php);
assert(!HeapEmpty(php));
return php->a[0];
}
Empty
c
bool HeapEmpty(HP* php) {
assert(php);
return php->size == 0;
}
Size
c
int HeapSize(HP* php) {
assert(php);
return php->size;
}
AdjustUp
c
void AdjustUp(HPDataType* a, int child) {
int parent = (child - 1) / 2;
while (child > 0) {
if (a[child] < a[parent]) {// 小堆
Swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else {
break;
}
}
}
AdjustDown
c
void AdjustDown(HPDataType* a, int n, int parent) {// n为数据个数
int child = parent * 2 + 1;
while (child < n) {
if (child + 1 < n && a[child] > a[child + 1]) {
child++;
}
if (a[child] > a[parent]) {// max
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else {
break;
}
}
}
4. HeapSort
c
void HeapSort(int* a, int n) {
// 升序--建大堆 max
// 建堆--向下调整堆--O(N)
for (int i = (n - 1 - 1) / 2; i >= 0; --i) {
AdjustDown(a, n, i);
}
int end = n - 1;
while (end > 0) {
Swap(&a[0], &a[end]);
// 把堆顶max放在最后
// 再调整,选出次小的,升序
AdjustDown(a, end, 0);
--end;
}
}
5. Top-K
给定一个长度为 𝑛 的无序数组 nums ,请返回数组中最大的 𝑘 个元素。
Top‑k 是一个经典算法问题,可以使用堆数据结构高效解决,时间复杂度为 𝑂(𝑛 log 𝑘)
方案:
- 初始化一个小顶堆,其堆顶元素最小。
- 先将数组的前 𝑘 个元素依次入堆。
- 从第 𝑘 + 1 个元素开始,若当前元素大于堆顶元素,则将堆顶元素出堆,并将当前元素入堆。
- 遍历完成后,堆中保存的就是最大的 𝑘 个元素。


c
void CreateNData() {
int n = 1000;
srand(time(0));
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
for (size_t i = 0; i < n; i++) {
int x = rand() % 10000;
fprintf(fin, "%d\n", x);
}
fclose(fin);
}
c
void PrintTopK(int k) {
const char* file = "data.txt";
FILE* fout = fopen(file, "r");
int* kminheap = (int*)malloc(sizeof(int) * k);
for (int i = 0; i < k; i++) {
fscanf(fout, "%d", &kminheap[i]);
}
for (int i = (k - 1 - 1) / 2; i >= 0; i--) {
AdjustDown(kminheap, k, i);
}
int val = 0;
while (!feof(fout)) {//未到达文件末尾,feof返回零。
fscanf(fout, "%d", &val);
if (val > kminheap[0]) {
kminheap[0] = val;
AdjustDown(kminheap, k, 0);
}
}
for (int i = 0; i < k; i++) {
printf("%d ", kminheap[i]);
}
printf("\n");
}