[数据结构]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");
}
相关推荐
S01d13r1 小时前
LeetCode 解题思路 48(编辑距离、只出现一次的数字)
算法·leetcode·职场和发展
C_Liu_1 小时前
C语言:深入理解指针(5)
java·c语言·算法
small_wh1te_coder1 小时前
从经典力扣题发掘DFS与记忆化搜索的本质 -从矩阵最长递增路径入手 一步步探究dfs思维优化与编程深度思考
c语言·数据结构·c++·stm32·算法·leetcode·深度优先
枫景Maple1 小时前
LeetCode 45. 跳跃游戏 II(中等)
算法·leetcode
এ᭄画画的北北1 小时前
力扣-236.二叉树的最近公共祖先
算法·leetcode
z人间防沉迷k3 小时前
堆(Heap)
开发语言·数据结构·笔记·python·算法
hy.z_7773 小时前
【数据结构】链表 LinkedList
java·数据结构·链表
不二狗3 小时前
每日算法 -【Swift 算法】Two Sum 问题:从暴力解法到最优解法的演进
开发语言·算法·swift
LabVIEW开发3 小时前
LabVIEW中样条插值实现及应用
算法·labview知识
ROCKY_8173 小时前
数据结构——例题3
数据结构