[数据结构]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");
}
相关推荐
努力学习的小廉19 小时前
我爱学算法之—— 模拟(下)
c++·算法
GilgameshJSS20 小时前
STM32H743-ARM例程9-IWDG看门狗
c语言·arm开发·stm32·单片机·嵌入式硬件·学习
Hello_Embed20 小时前
STM32 智能垃圾桶项目笔记(一):超声波模块(HC-SR04)原理与驱动实现
c语言·笔记·stm32·单片机·嵌入式软件·嵌入式项目
菠萝地亚狂想曲20 小时前
极简文件列表
c语言
海琴烟Sunshine20 小时前
Leetcode 26. 删除有序数组中的重复项
java·算法·leetcode
PAK向日葵20 小时前
【算法导论】NMWQ 0913笔试题
算法·面试
PAK向日葵20 小时前
【算法导论】DJ 0830笔试题题解
算法·面试
PAK向日葵20 小时前
【算法导论】LXHY 0830 笔试题题解
算法·面试
麦麦麦造21 小时前
DeepSeek突然发布 V3.2-exp,长文本能力加强,价格进一步下探
算法
jinmo_C++21 小时前
数据结构_ 二叉树线索化:从原理到手撕实现
数据结构