[数据结构]7. 堆-Heap
堆-Heap
- 1. 介绍
- 2. 堆的实现
- 3. 堆操作
- Initlilze
- Destory
- Swap
- Push
- Pop
- Top
- Empty
- Size
- AdjustUp
- AdjustDown
- 4. HeapSort
- 5. Top-K
1. 介绍
堆(heap) 是一种满足特定条件的完全二叉树。
- 小顶堆(min heap):任意节点的值 ≤ 其子节点的值。
- 大顶堆(max heap):任意节点的值 ≥ 其子节点的值
堆作为完全二叉树的一个特例,具有以下特性。
- 最底层节点靠左填充,其他层的节点都被填满。
- 将根节点称为“堆顶”,将底层最靠右的节点称为“堆底”。
- 大顶堆,根节点值是最大的。
- 小顶堆,根节点值是最小的。
2. 堆的实现
完全二叉树非常适合用数组来表示。
使用数组表示二叉树时,元素代表节点值,索引代表节点在二叉树中的位置。
节点指针通过索引映射公式来实现:
给定索引 𝑖 ,其左子节点的索引为 2𝑖 + 1 ,右子节点的索引为 2𝑖 + 2 ,父节点的索引为(𝑖 - 1)/2(向下整除)。当索引越界时,表示空节点或节点不存在。
3. 堆操作
Initlilze
void HeapInit(HP* php) {assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}
Destory
void HeapDestory(HP* php) {assert(php);free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}
Swap
void Swap(HPDataType* p1, HPDataType* p2) {HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
Push
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
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
HPDataType HeapTop(HP* php) {assert(php);assert(!HeapEmpty(php));return php->a[0];
}
Empty
bool HeapEmpty(HP* php) {assert(php);return php->size == 0;
}
Size
int HeapSize(HP* php) {assert(php);return php->size;
}
AdjustUp
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
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]) {// maxSwap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else {break;}}
}
4. HeapSort
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 个元素开始,若当前元素大于堆顶元素,则将堆顶元素出堆,并将当前元素入堆。
- 遍历完成后,堆中保存的就是最大的 𝑘 个元素。
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);
}
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");
}