[数据结构]7. 堆-Heap

堆-Heap

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. 初始化一个小顶堆,其堆顶元素最小。
  2. 先将数组的前 𝑘 个元素依次入堆。
  3. 从第 𝑘 + 1 个元素开始,若当前元素大于堆顶元素,则将堆顶元素出堆,并将当前元素入堆。
  4. 遍历完成后,堆中保存的就是最大的 𝑘 个元素。
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");
}
相关推荐
星恒随风2 分钟前
Linux进程信号(一):从Ctrl+C到kill、raise、alarm,深入理解信号的概念与产生原理
linux·c语言·笔记·学习·状态模式
碧海银沙音频科技研究院7 分钟前
基于炬芯ATS3089X音箱的音乐播放歌词显示功能实现
人工智能·驱动开发·嵌入式硬件·算法
学逆向的18 分钟前
C 浮点数e(科学计数法)
c语言·开发语言·网络安全
知识分享小能手20 分钟前
C学习教程,从入门到精通,C语言数组 —— 知识点详解(8)
c语言·学习·算法
hetao173383723 分钟前
联盟场-提高组-R2 & ZYZSC-S-Round 5
c++·算法
lhbweilai31 分钟前
分析FreeRTOS内核对象API的源码实现(三)
c语言·stm32·单片机
其实防守也摸鱼34 分钟前
Codex 下载与本地部署实战指南
linux·服务器·前端·数据库·算法·ai
南极企鹅36 分钟前
生产环境gc文件分析
java·jvm·算法
希洛兔38 分钟前
原码、反码、补码:一张图讲透计算机里的负数表示
算法·面试
benchmark_cc39 分钟前
批量获取股票数据后,如何统一转换成 DataFrame?从数据结构到量化研究的完整实践
数据结构·python·数据分析·量化交易·股票数据·quantdash