每日回顾:简单用C搓个数组堆

heap.h------标准库包含、函数声明

heap.c------函数定义

main.c------测试

堆的逻辑结构是一颗完全二叉树:

heap.h

heap.h中应包含如下定义和功能:

cpp 复制代码
#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

// 堆初始化
void HeapInit(Heap* php);
// 堆的销毁
void HeapDestory(Heap* hp);

//向上调整
void AdjustUp(HPDataType* a, int child);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);

//向下调整
void AdjustDown(HPDataType* a, int n, int parent);
// 堆的删除(删除堆顶元素)
void HeapPop(Heap* hp);

// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
bool HeapEmpty(Heap* hp);

heap.c

逐个在 heap.c 中实现上述功能,同时保证代码的健壮性

heap.c 中包含 heap.h

1、堆初始化

cpp 复制代码
// 堆初始化
void HeapInit(Heap* php) {
	assert(php);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}

2、堆的销毁

cpp 复制代码
// 堆的销毁
void HeapDestory(Heap* hp) {
	assert(hp);
	free(hp->a);
}

3、向上调整

cpp 复制代码
//交换元素
void Swap(HPDataType* p1, HPDataType* p2) {
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//向上调整
void AdjustUp(HPDataType* a, int child) {
	int parent = (child - 1) / 2;
	//只要 child 有效,都会进入循环判断
	while (child > 0) {
		//建立大堆
		if (a[child] > a[parent]) {
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else {	//已满足堆的条件
			break;
		}
	}
}

4、堆的插入

cpp 复制代码
// 堆的插入
void HeapPush(Heap* hp, HPDataType x) {
	assert(hp);
	//检查容量,如果已满,扩容
	if (hp->size == hp->capacity) {
		int newCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newCapacity);
		if (tmp == NULL) {
			perror("realloc failed");
			return;
		}
		hp->a = tmp;
		hp->capacity = newCapacity;
	}

	hp->a[hp->size++] = x;
	//尾插新数据后,向上调整
	//传入数组和孩子下标
	AdjustUp(hp->a, hp->size - 1);
}

5、向下调整

cpp 复制代码
//向下调整
void AdjustDown(HPDataType* a, int n, int parent) {
	int child = parent * 2 + 1;	//左孩子
	//调整时,parent 要与 左右孩子中大的那个交换--
	//--以保证满足大堆条件

	while (child < n) {
		//选出左右孩子中较大者
		if ((child + 1) < n && a[child] < a[child + 1]) {
			child++;
		}

		if (a[parent] < a[child]) {
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {	//已满足大堆条件
			break;
		}
	}
}

6、堆的删除

cpp 复制代码
// 堆的删除(删除堆顶元素)
void HeapPop(Heap* hp) {
	//交换堆顶和堆底元素,删除堆底元素,向下调整
	assert(hp);
	assert(!HeapEmpty(hp));	//堆不能为空

	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	AdjustDown(hp->a, hp->size, 0);
}

7、取堆顶元素

cpp 复制代码
// 取堆顶的数据
HPDataType HeapTop(Heap* hp) {
	assert(hp);
	assert(!HeapEmpty(hp));	//堆不能为空
	return hp->a[0];
}

8、堆的数据个数

cpp 复制代码
// 堆的数据个数
int HeapSize(Heap* hp) {
	assert(hp);
	return hp->size;
}

9、判断堆空

cpp 复制代码
// 堆的判空
bool HeapEmpty(Heap* hp) {
	assert(hp);
	return hp->size == 0;
}

main.c

cpp 复制代码
#include"heap.h"

void Test1() {
	Heap hp;
	HeapInit(&hp);

	HeapPush(&hp, 2);
	HeapPush(&hp, 1);
	HeapPush(&hp, 3);
	HeapPush(&hp, 6);
	HeapPush(&hp, 5);
	HeapPush(&hp, 9);
	HeapPush(&hp, 2);

	for (int i = 0; i < hp.size; i++) {
		printf("%d ", hp.a[i]);
	}
	printf("\n");

	HeapPop(&hp);
	for (int i = 0; i < hp.size; i++) {
		printf("%d ", hp.a[i]);
	}

	HeapDestory(&hp);
}

int main() {
	Test1();
	return 0;
}

将存储的数据拆成完全二叉树后,仍然是大堆,没有问题

相关推荐
草莓熊Lotso5 分钟前
【C语言操作符详解(一)】--进制转换,原反补码,移位操作符,位操作符,逗号表达式,下标访问及函数调用操作符
c语言·经验分享·笔记
猫猫头有亿点炸32 分钟前
C语言大写转小写2.0
c语言·开发语言
无敌的牛41 分钟前
AVL树的介绍与学习
数据结构·学习
想不明白的过度思考者1 小时前
初识数据结构——二叉树从基础概念到实践应用
数据结构·二叉树
大鱼YY1 小时前
C语言内敛函数
c语言·内联函数
luoqice2 小时前
基于esp32实现键值对存储读写c程序例程
c语言·开发语言
Brookty3 小时前
【数据结构】哈希表
数据结构·算法·哈希算法·散列表
阿方.9183 小时前
C语言----函数栈帧讲解
c语言·开发语言
Dovis(誓平步青云)4 小时前
【数据结构】·励志大厂版(复习+刷题):二叉树
c语言·数据结构·经验分享·笔记·学习·算法·学习方法
越城4 小时前
算法效率的钥匙:从大O看复杂度计算 —— C语言数据结构第一讲
c语言·开发语言·数据结构·算法