【C语言】数据结构#实现堆


目录

(一)堆

(1)堆区与数据结构的堆

(二)头文件

(三)功能实现

(1)堆的初始化

(2)堆的销毁

(3)插入数据

(4)删除堆顶的数据

(5)得到堆顶的数据

(6)判断堆是否为空

(7)得到堆内数据个数


正文开始:

(一)堆

(1)堆区与数据结构的堆

堆区和数据结构中的堆是两个不同的概念。

  1. 堆区 (Heap) :堆区是计算机内存中的一部分,用于存储动态分配的内存空间。在程序运行时,堆区用于存储使用 new 或 malloc 等方法分配的内存空间,在程序运行结束后,由程序员手动释放。堆区是一块较大的内存区域,用于存储动态分配的数据。堆区的大小可以动态增长或缩小,具有较高的灵活性。堆区的访问速度较慢,但能够存储较大的数据。

  2. 数据结构中的堆:在数据结构中,堆是一种特殊的树状结构,具有以下特点:

  • 堆是一个完全二叉树,即除了最底层外,其他层都是满的,最底层的结点从左到右连续排列。
  • 堆中的每个结点的值都大于等于(或小于等于)其子结点的值,根结点是树中最大(或最小)的结点。
  • 堆可以分为最大堆和最小堆,最大堆的根结点是整个堆中最大的元素,最小堆的根结点是整个堆中最小的元素。

在数据结构中,堆通常用于实现优先队列(Priority Queue)和堆排序(Heap Sort)等算法。堆的插入和删除操作的时间复杂度都为 O(log n),其中 n 是堆中元素的个数。

(二)头文件

STL(Standard Template Library)是C++标准库中的一个组件,提供了一系列的通用数据结构和算法,以及一些函数模板,用于简化C++程序的开发。STL包括了容器(Containers)、算法(Algorithms)和迭代器(Iterators)三个主要部分。

  1. 容器(Containers):STL提供了多种容器,包括向量(vector)、链表(list)、双端队列(deque)、集合(set)、映射(map)等。这些容器提供了不同的数据结构和操作,方便了数据的存储和处理。

  2. 算法(Algorithms):STL提供了一系列的算法,包括排序、查找、合并、变序、计数等等。这些算法可以对容器中的元素进行操作,使得程序更加高效和简洁。

  3. 迭代器(Iterators):STL的迭代器是一个泛型指针,用于遍历容器中的元素。迭代器提供了一种统一的访问容器元素的方式,使得算法可以独立于容器而使用。

本文根据Cpp的STL来实现堆的功能,包括堆的初始化,销毁,插入数据,删除堆顶的数据,得到堆顶的数据,判断堆是否为空,得到堆内数据个数等七个功能接口。

本文基于顺序表实现堆;

这里不加解释的给出头文件,根据头文件实现堆的功能:

命名:Heap.h

cpp 复制代码
#pragma once

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

typedef int HPDatatype;

typedef struct Heap
{
	HPDatatype* a;//存储数据的数组
	int size;     //堆内数据的个数
	int capacity; //顺序表的容量
}HP;

//初始化
void HPinit(HP* php);

//销毁
void HPDestroy(HP* php);

//插入数据
void HPpush(HP* php, HPDatatype x);

//删除数据,规定删除堆顶的数据
void HPpop(HP* php);

//得到堆顶数据
HPDatatype HPtop(HP* php);

//判空
bool HPempty(HP* php);

//数据个数
int HPsize(HP* php);

(三)功能实现

(1)堆的初始化

堆的初始化

首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

将数组置空,顺序表的大小与容量置0;

cpp 复制代码
//初始化
void HPinit(HP* php)
{
	assert(php);

	php->a = NULL;
	php->capacity = php->size = 0;
}

(2)堆的销毁

堆的销毁

首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

释放掉动态申请的顺序表内的数组,顺序表的大小与容量置0;

cpp 复制代码
//销毁
void HPDestroy(HP* php)
{
	assert(php);

	free(php->a);
	php->capacity = php->size = 0;

}

(3)插入数据

插入数据

首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

其次判断数组内数据是否满了

------如果顺序表容量等于数组的大小,代表数据满了,那么进行扩容。Newcapacity的赋值通过三目操作符实现:如果capacity为初始值0,Newcapacity赋值为4,否则赋值为2*capacity。如果realloc申请失败,打印错误信息并返回。若申请成功,压入数据。

------如果数据没有满,直接在数组中插入数据,由于插入的数据不一定与原数据成堆,所以要进行向上调整
调整需要交换,于是提前给出交换的功能接口:

交换

cpp 复制代码
//传址交换
void Swap(HPDatatype* p1, HPDatatype* p2)
{
	HPDatatype tem = *p1;
	*p1 = *p2;
	*p2 = tem;
}

什么是向上调整?

堆在逻辑上是二叉树,在物理上实际上是数组,数组的下标如下:

在堆中,有一个规律:

  • 父节点下标 = (子节点下标 - 1) / 2;
  • 左子节点下标 = (父结点下标 * 2) + 1;
  • 右子节点下标 = (父结点下标 * 2) + 2;

于是,我们可以通过一个节点的下标,找到他的父和子的下标 ;

本文以实现小堆为例

向上调整

将新插入的节点与其父节点比较,如果新节点小于父结点,两节点交换值,继续迭代进行;

直到新节点的值大于等于父结点,或者已经比到了根节点才停止;

cpp 复制代码
//push实现小堆的向上调整
void AdgustUP(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;
		}
	}
}

插入数据

cpp 复制代码
//插入数据
void HPpush(HP* php, HPDatatype x)
{
	assert(php);
	//数据满,realloc扩容,得到新的大容量顺序表
	if (php->capacity == php->size)
	{
		int Newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDatatype* tem = (HPDatatype*)realloc(php->a, Newcapacity * sizeof(HPDatatype));
		if (tem == NULL)
		{
			perror("realloc fail!");
			return;
		}
		php->a = tem;
		php->capacity = Newcapacity;
	}

	//数据不满,直接插入
	php->a[php->size] = x;
	php->size++;

	//向上调整
	AdgustUP(php->a,php->size-1);
}

(4)删除堆顶的数据

删除数据

删除数据规定的是删除堆顶的数据;

函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

如何删除?

-直接删除,由于二叉树的兄弟节点是没有大小关系的,如果直接删除根节点,那么所有节点的下标减一,这意味着原来的的二叉树的结构就被完全破坏了,接下来只能重新建堆,代价太大。

-先交换堆顶与最后一个数据,然后删除最后一个数据(其实就是原堆顶数据),然后进行向下调整;

这样既没有完全破换二叉树的结构,只有一个数据需要调整位置,又操作简便只需size--即可。

向下调整

思路与向上调整基本一致;

cpp 复制代码
//小堆向下调整------找小
void AdgustDown(HPDatatype* a, int n, int parent)
{
	//假设左孩子小
	int child = parent * 2 + 1;
	while (child < n)
	{
		//如果右孩子小,假设不成立
		if (child + 1 < n && a[child] > a[child + 1])
		{
			child++;
		}
		//此时,child表示较小的孩子
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}

	}

}

删除数据

cpp 复制代码
//删除数据,规定删除堆顶的数据
void HPpop(HP* php)
{
	assert(php);
	//交换堆顶与最后一个数据
	Swap(&php->a[0], &php->a[php->size - 1]);
	//删除堆顶的数据
	php->size--;
	//向下调整
	AdgustDown(php->a, php->size,0);

}

(5)得到堆顶的数据

首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

其次,堆不为空,通过assert断言实现;

直接返回堆顶的数据即可;

cpp 复制代码
//得到堆顶数据
HPDatatype HPtop(HP* php)
{
	assert(php);
	assert(!HPempty(php));

	return php->a[0];
}

(6)判断堆是否为空

首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

直接返回判断表达式的值即可;(若为空,返回真(1);否则返回假(0))

cpp 复制代码
//判空
bool HPempty(HP* php)
{
	assert(php);
	return php->size == 0;
}

(7)得到堆内数据个数

首先,函数接收的指针(被传入的地址)不为空(否则无法进行解引用操作),通过assert断言实现;

直接返回堆内数据个数即可;

cpp 复制代码
//数据个数
int HPsize(HP* php)
{
	assert(php);

	return php->size;
}

完~

未经作者同意禁止转载

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