C语言实现堆

📋 个人简介

  • 💖 作者简介:大家好,我是菀枯😜

  • 🎉 支持我:点赞👍+收藏⭐️+留言📝

  • 💬格言:不要在低谷沉沦自己,不要在高峰上放弃努力!☀️

前言

上一次我们学习了一些树的基础概念,树的性质等。今天我们试着用C语言来实现一种数据结构:

一. 什么是堆

堆的概念

n个元素的序列 k 1 , k 2 . . . , k n {k_1,k_2..., k_n} k1,k2...,kn当且仅当满足下关系时,称之为堆。
( k i ≤ k 2 i 且 k i ≤ k 2 i + 1 ) 或 者 ( k i ≥ k 2 i 且 k i > k 2 i + 1 ) , i = [ 1 , 2 n ] (k_i\leq k_{2i} 且 k_i \leq k_{2i + 1}) 或者(k_i \geq k_{2i} 且 k_i > k_{2i+1}),i=[1,\frac{2}{n}] (kik2ikik2i+1)(kik2iki>k2i+1),i=[1,n2]
若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右子结点的值。由此,若序列 k 1 , k 2 . . . k n {k_1, k_2...k_n} k1,k2...kn是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值).

堆的性质

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树。

堆的分类

根据堆中根元素的大小我们可将堆分为两类

  • 根元素若为堆中最大元素,我们将此堆称为大根堆

    image-20220402102335135 image-20220402102516552
  • 若根中元素为堆中最小元素, 我们就称此堆为小根堆

    image-20220402102848124

    image-20220402102910237

二. 堆的实现

2.1 堆的头文件

1. 包含的标准库
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
2. 定义结构体
typedef int HeapDType; (1)

typedef struct Heap (2)
{
	HeapDType* val;
	int count;
	int capacity;
}Heap;
  1. 堆中存放的数据类型。
  2. Heap中有一个指向存放元素的数组的指针,记录元素个数的count, 记录数组容量的capacity。
3.函数声明
void HeapInti(Heap* pheap);
// 堆的初始化
void HeapDestory(Heap* pheap);
// 堆的销毁
void HeapPop(Heap* pheap);
// 删除根的元素
void HeapPush(Heap* pheap, HeapDType x);
// 将x的加入进堆中
bool HeapEmpty(Heap* pheap);
// 判断堆是否为空
void HeapPrint(Heap* pheap);
// 显示堆中元素
HeapDType HeapTop(Heap* pheap);
// 取根元素
int HeapSize(Heap* pheap);
// 堆的大小

2.2 函数实现

1. 堆的初始化
void HeapInti(Heap* pheap)
{
	assert(pheap); //(1)

	pheap->val = NULL; (2)
	pheap->capacity = pheap->count = 0;
}
  1. 防止pheap为空指针
  2. 将val置为空指针, 将count 和 capacity清0
2. 堆的销毁
void HeapDestory(Heap* pheap)
{
	assert(pheap); 
	free(pheap->val); //(1)
	pheap->val = NULL;  //(2)
	pheap->capacity = pheap->count = 0;
}
  1. 释放掉数组的空间
  2. 将val置为空指针, 将count 和 capacity清0
3. 将新元素放入堆中

与之前顺序表的添加,链表的添加不同,堆的添加会需要改变堆的结构。我们先来看一个动画,看看堆插入元素的思路:

1
  1. 将新元素放到堆的最下面。
  2. 判读新元素是否比父亲节点里的元素小
  3. 如果小,就交换子节点和父亲节点,否则循环结束。

代码实现:

void HeapPush(Heap* pheap, HeapDType x)
{
	assert(pheap);
	if (pheap->count == pheap->capacity) //(1)
	{
		int newCapacity = pheap->capacity == 0 ? 4 : pheap->capacity * 2;
		HeapDType* tmp = (HeapDType*)realloc(pheap->val, sizeof(HeapDType) * newCapacity);
		if (NULL == tmp) //(2)
		{
			printf("realloc error\n");
			exit(-1);
		}
		pheap->capacity = newCapacity;
		pheap->val = tmp;
	}

	pheap->val[pheap->count++] = x;  //(3)
	AdjustTop(pheap->val, pheap->count - 1); 
}
  1. 判断是否需要对原数组进行扩容
  2. 如果开辟失败,退出程序
  3. 将新元素放到堆尾,然后开始向上进行调整
void AdjustTop(HeapDType* val, int child)
{
	int parent = (child - 1) / 2; //(1)
	while (child > 0)
	{
		if (val[child] < val[parent]) //(2)
		{
			Swap(&(val[child]), &(val[parent]));
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void Swap(HeapDType* px, HeapDType* py) //(3)
{
	HeapDType tmp = *px;
	*px = *py;
	*py = tmp;
}
  1. 找子节点的父亲节点。
  2. 判断是否需要交换子节点和父亲节点
  3. 交换子节点和父亲节点
4. 删除根元素

堆的删除,我们无法用以前顺序表的那种方式直接用后面的数据覆盖前面的数据,原因如下:

  • 会破坏堆原有的数据结构
  • 时间复杂度为 O(n), 时间复杂度太高

所以我们需要一种新的方法,来删除根处元素,大家可以仔细思考一下,用什么样的方法比较好。
1648871714053

思路如下(看动图):

2
  1. 将根节点与最后一个叶子节点交换。
  2. 删除最后一个叶节点。
  3. 将父亲节点和左右子节点中较小的一个进行比较。
  4. 若父亲节点大于子节点,则进行交换,直至子节点都大于父亲节点。
void HeapPop(Heap* pheap)
{
	assert(pheap); 

	if (!HeapEmpty(pheap)) //(1)
	{
		Swap(&(pheap->val[0]), &(pheap->val[pheap->count - 1])); //(2)
		pheap->count--;
		AdjustDown(pheap->val, pheap->count, 0); //(3)
	}
}
  1. 判断堆是否为空,不为空则进行删除
  2. 将根节点与最后一个叶节点交换
  3. 向下进行调整
void AdjustDown(HeapDType* val, int size, int parent)
{
	int child = 2 * parent + 1; //(1)
	while (child < size)
	{
		if (val[child] > val[child + 1]) //(2)
		{
			child++;
		}

		if (val[parent] > val[child] && child <size) //(3)
		{
			Swap(&val[parent], &val[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}
  1. 找父亲节点的左子节点。
  2. 若右子节点小于左子节点,则让child为右子节点。
  3. 交换子节点和父亲节点。
5. 判断堆是否为空
bool HeapEmpty(Heap* pheap)
{
	assert(pheap);
	return pheap->count == 0;
}

判断堆中元素是否为空即可

6. 求堆的大小
int HeapSize(Heap* pheap)
{
	assert(pheap);
	return pheap->count; 
}

返回堆中元素个数

7. 取根元素
HeapDType HeapTop(Heap* pheap)
{
	assert(pheap);
	assert(pheap->count > 0); //(1)
	return pheap->val[0];
}
  1. 防止堆为空。
8. 打印堆中元素
void HeapPrint(Heap* pheap)
{
	assert(pheap);
	for (int i = 0; i < pheap->count; i++)
	{
		printf("%d ", pheap->val[i]);
	}
}

遍历堆中元素,打印即可。

三. 完整代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int HeapDType;

typedef struct Heap
{
	HeapDType* val;
	int count;
	int capacity;
}Heap;

void HeapInti(Heap* pheap)
{
	assert(pheap);

	pheap->val = NULL;
	pheap->capacity = pheap->count = 0;
}

bool HeapEmpty(Heap* pheap)
{
	assert(pheap);
	return pheap->count == 0;
}

void HeapDestory(Heap* pheap)
{
	assert(pheap);
	free(pheap->val);
	pheap->val = NULL;
	pheap->capacity = pheap->count = 0;
}

void Swap(HeapDType* px, HeapDType* py)
{
	HeapDType tmp = *px;
	*px = *py;
	*py = tmp;
}

void AdjustTop(HeapDType* val, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (val[child] < val[parent])
		{
			Swap(&(val[child]), &(val[parent]));
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void HeapPush(Heap* pheap, HeapDType x)
{
	assert(pheap);
	if (pheap->count == pheap->capacity)
	{
		int newCapacity = pheap->capacity == 0 ? 4 : pheap->capacity * 2;
		HeapDType* tmp = (HeapDType*)realloc(pheap->val, sizeof(HeapDType) * newCapacity);
		if (NULL == tmp)
		{
			printf("realloc error\n");
			exit(-1);
		}
		pheap->capacity = newCapacity;
		pheap->val = tmp;
	}

	pheap->val[pheap->count++] = x;
	AdjustTop(pheap->val, pheap->count - 1);
}

void HeapPrint(Heap* pheap)
{
	assert(pheap);
	for (int i = 0; i < pheap->count; i++)
	{
		printf("%d ", pheap->val[i]);
	}
}

void AdjustDown(HeapDType* val, int size, int parent)
{
	int child = 2 * parent + 1;
	while (child < size)
	{
		if (child + 1 < size && val[child] > val[child + 1])
		{
			child++;
		}

		if (val[parent] > val[child] && child <size)
		{
			Swap(&val[parent], &val[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapPop(Heap* pheap)
{
	assert(pheap);

	if (!HeapEmpty(pheap))
	{
		Swap(&(pheap->val[0]), &(pheap->val[pheap->count - 1]));
		pheap->count--;
		AdjustDown(pheap->val, pheap->count, 0);
	}
}

HeapDType HeapTop(Heap* pheap)
{
	assert(pheap);
	assert(pheap->count > 0);
	return pheap->val[0];
}

int HeapSize(Heap* pheap)
{
	assert(pheap);
	return pheap->count;
}

结语

欢迎各位参考与指导!!!博主最近在冲击C/C++领域新人,拜托大家帮忙点赞收藏一下❤️

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
好的,下面是使用C语言实现排序的示例代码: ```c #include <stdio.h> void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } void heapify(int arr[], int size, int i) { int largest = i; // 根节点索引 int left = 2 * i + 1; // 左子节点索引 int right = 2 * i + 2; // 右子节点索引 // 如果左子节点大于根节点,则将largest更新为左子节点索引 if (left < size && arr[left] > arr[largest]) { largest = left; } // 如果右子节点大于当前最大值,则将largest更新为右子节点索引 if (right < size && arr[right] > arr[largest]) { largest = right; } // 如果最大值不是当前节点,则交换当前节点与最大值节点,并递归调整 if (largest != i) { swap(&arr[i], &arr[largest]); heapify(arr, size, largest); } } void heapSort(int arr[], int size) { // 构建最大,从最后一个非叶子节点开始向上调整 for (int i = size / 2 - 1; i >= 0; i--) { heapify(arr, size, i); } // 依次将最大值(根节点)移动到数组末尾,并调整 for (int i = size - 1; i > 0; i--) { swap(&arr[0], &arr[i]); // 将最大值移动到末尾 heapify(arr, i, 0); // 调整 } } int main() { int arr[] = {8, 4, 2, 9, 3, 6}; int size = sizeof(arr) / sizeof(arr[0]); heapSort(arr, size); printf("排序后的数组:"); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } return 0; } ``` 在这个示例代码中,我们首先构建一个最大,然后将根节点(最大值)与数组末尾交换,并将剩余元素重新调整为最大。通过不断重复这个过程,最终实现排序。 希望这个示例代码对您有所帮助!如果您有任何问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

。菀枯。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值