堆的实现(C语言版)

在这里插入图片描述

文章目录

  • 概述
  • 堆的实现
    • 初始化
    • 销毁
    • 插入
    • 删除
    • 取堆顶元素
    • 求堆的长度
    • 判断堆是否为空
  • 完整代码

概述

如果有一个关键码的集合K = {k0,k1,k2…kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <=K2*i+1 且 Ki<=K2*i+2 (Ki >= K2*i+1且 Ki>= K2*i+1) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。堆的性质:

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

在这里插入图片描述

堆的实现

初始化

堆的存储结构是一个数组,堆的初始化需要定义一个数组,当前元素个数和容量。和顺序表的初始化一样。

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

void HeapInit(HP* php)
{
	assert(php);

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

销毁

释放数组a的空间,将php->capacity = php->size = 0

void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->capacity = php->size = 0;
}

插入

堆的插入是先在数组的最后插入元素,但是需要满足堆的特点(大堆或小堆),因此需要用到向上调整算法,来实现这一特点。

介绍向上调整算法

这里小编以实现小堆为例

在数组的最后插入一个元素child,然后这个元素与其双亲节点parent进行比较:

  • 如果 child>parent:满足小堆的条件,无需交换
  • 如果 child<parent:不满足小堆条件,此时需要将孩子节点child与它的双亲结点parent进行交换,此时原来的双亲结点parent变成了孩子结点child,原来的孩子节点child变成了双亲结点parent。此时,再让现在的双亲结点parent和它的双亲结点parent进行比较,如果不满足小堆,则继续交换,继续比较
  • 循环结束的条件是child>0

举个例子:

如下,在堆中插入元素10:

在这里插入图片描述
将10与它的双亲结点进行比较,10<28,不满足小堆的条件,将10和28,进行交换:

在这里插入图片描述

交换完后,此时的10变成了28的双亲结点,28变成了10的孩子结点。现在再将10与它的双亲结点比较,10<18,不满足小堆的特点,继续交换。

在这里插入图片描述

交换完后10变成了18的双亲结点,18变成了10的孩子结点。10和它的双亲结点比较,依然不满足小堆条件,继续交换

在这里插入图片描述

此时,10已经变成了根节点,并且满足小堆的条件,循环结束。

看了图解,对向上调整算法有了大概的印象,但是代码的编写,还需要再去分析一下。

定义parent是孩子的双亲结点,双亲结点parent与孩子结点child满足parent = (child - 1) / 2关系。进入循环,比较孩子节点的值和双亲结点的值,判断是否满足小堆的条件。

void swap(HPDataType* p1, HPDataType* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

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 = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

写完向上调整算法,便可实现插入操作

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));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newCapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

删除

在删除操作里面,一般规定删除堆顶,即根节点

在这里插入图片描述

删除根节点的常规操作是将根结点和最后一个叶节点进行交换,然后尾删即可,此时根节点的左右子树依然是小堆

在这里插入图片描述
但是根节点不满足小队的条件,因此引入向下调整算法

向下调整算法:

向上调整算法是一个道理

但是此时根节点是双亲结点,有两个孩子,不知道该选择哪一个孩子。这里使用到了假设法:假设左孩子小,如果假设错了,更新一下

判断双亲结点和孩子结点的大小:

  • 如果双亲结点小于孩子结点,直接结束
  • 如果双亲结点大于孩子结点,交换双亲结点和孩子结点的值,然后更新一下双亲结点的位置和孩子节点的位置

循环结束的条件是child<size

和向上调整算法基本一致,直接上代码:

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

		if (a[child] < a[parent])
		{
			swap(a[parent], a[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

删除操作:

void HeapPop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

取堆顶元素

先判断堆是否存在,然后直接返回堆顶元素即可

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}

求堆的长度

先判断堆是否存在,直接返回堆的长度即可

size_t HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

判断堆是否为空

先判断堆是否存在,如果php->size==0,那么堆为空,返回true,反之返回false

bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

完整代码

Heap.h

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

typedef int HPDataType;

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

void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);
// 规定删除堆顶(根节点)
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
size_t HeapSize(HP* php);
bool HeapEmpty(HP* php);

Heap.c

# define _CRT_SECURE_NO_WARNINGS


#include"Heap.h"

void HeapInit(HP* php)
{
	assert(php);

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

void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->capacity = php->size = 0;
}

void swap(HPDataType* p1, HPDataType* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

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 = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

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));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newCapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

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

		if (a[child] < a[parent])
		{
			swap(a[parent], a[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void HeapPop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}

size_t HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/184223.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

拼多多平台全面API接口对接

对接流程&#xff08;支持虚拟商品&#xff09; 拼多多与商家之间数据双向请求&#xff0c;同步更新及相关数据传输。对接主要分为三大部分&#xff1a;准备阶段、对接测试、上线使用&#xff1b;针对每部分具体说明如下&#xff1a; 接口连通性测试重点关注两类接口的连通性&a…

【数据结构】最小生成树(Kruskal算法)

一.基本思想 设无向连通网为G&#xff08;V&#xff0c;E&#xff09;&#xff0c;令G的最小生成树为T&#xff08;U&#xff0c;TE&#xff09;&#xff0c;其初态为UV&#xff0c;TE{},然后&#xff0c;按照边的权值由小到大的顺序&#xff0c;考察G的边集E中的各条边。若被考…

​飞凌嵌入式FCU2601网关,为工商业储能EMS注入智慧的力量

一、火热的储能行业&#xff0c;寻求新的市场机会 最近一段时间以来&#xff0c;世界储能大会、上海储能展、能源电子产业发展大会等多个储能相关论坛和展览密集登场&#xff0c;即使“内卷”已成为了业内讨论的热词&#xff0c;但寻求新的市场机会仍然是行业共识&#xff0c;…

「德州仪器嵌入式技术创新发展研讨会」落幕,飞凌嵌入式携手TI推动技术创新

11月22日&#xff0c;德州仪器嵌入式技术创新发展研讨会&#xff08;北京站&#xff09;顺利举行&#xff0c;本次研讨会邀请了众多业界领先的企业和专家到场&#xff0c;飞凌嵌入式作为TI生态伙伴受邀参加&#xff0c;与众多业内伙伴共话嵌入式技术的未来发展趋势。 在本次研…

Linux进程间通信

进程间通信介绍 首先进程是具有独立性的&#xff0c;要让两个不同的进程&#xff0c;进行通信&#xff0c;前提是&#xff1a;先让两个进程&#xff0c;看到同一份资源&#xff0c;这份资源及不能属于进程A也不能属于进程B&#xff0c;所以只能有操作系统直接或间接提供&#…

OkHttpUrlConnection库编写代码示例

OkHttpUrlConnection库编写的爬虫程序&#xff0c;该程序使用Kotlin编写的。 kotlin import java.net.HttpURLConnection import java.net.URL import java.net.URLConnection import java.io.BufferedReader import java.io.InputStreamReader fun main() { val url UR…

JVM中如何实现垃圾收集

Java虚拟机&#xff08;JVM&#xff09;使用垃圾收集器&#xff08;Garbage Collector&#xff09;来管理内存&#xff0c;清理不再使用的对象以释放内存空间。垃圾收集的主要目标是自动化内存管理&#xff0c;使开发人员无需显式地释放不再使用的内存&#xff0c;从而降低了内…

抖音本地生活服务商申请入口关闭?聚合服务商将成本地生活新模式

近年来&#xff0c;随着抖音本地生活服务为用户提供了便捷的生活方式相继支付宝、微信陆续推出了本地生活服务。然而&#xff0c;对于许多创业者而言&#xff0c;申请成为抖音本地生活服务商却面临着一定的门槛。因此&#xff0c;如何降低这些门槛&#xff0c;让更多的商家能够…

notion 3.0.0 版本最新桌面端汉化教程,支持MAC和WIN版本

notion客户端汉化&#xff08;目前版本3.0.0&#xff09; 最近notion桌面端更新了3.0.0版本后会导致老版本汉化失效&#xff0c;本项目实现了最新版Notion桌面端的汉化。 文件下载地址&#xff1a;汉化文件下载地址 项目说明 本项目针对新的客户端做了汉化文化&#xff0c;依…

ke12Servlet规范有三个高级特性,,文件上传下载

1Servlet规范有三个高级特性 分别是Filter、Listener和文件的上传下载。Filter用于修改request、response对象&#xff0c;Listener用于监听context、session、request事件。 熟悉Filter的生命周期 了解Filter及其相关API 掌握Filter的实现 掌握Filter的映射与过滤器链的使用…

第一个Mybatis项目

&#xff08;一&#xff09;为什么要用Mybatis? &#xff08;1&#xff09;Mybatis对比JDBC而言&#xff0c;sql&#xff08;单独写在xml的配置文件中&#xff09;和java编码分开&#xff0c;功能边界清晰&#xff0c;一个专注业务&#xff0c;一个专注数据。 &#xff08;2&…

java设计模式学习之【工厂模式】

文章目录 引言工厂方法模式简介定义与用途&#xff1a;实现方式&#xff1a; 使用场景优势与劣势工厂模式在spring中的应用电费计算示例&#xff08;简单工厂模式&#xff09;改善为方法工厂模式代码地址 引言 在软件开发的世界中&#xff0c;对象的创建可能是一个复杂且重复的…

【Git】一文教你学会 submodule 的增、删、改、查

添加子模块 $ git submodule add <url> <path>url 为想要添加的子模块路径path 为子模块存放的本地路径 示例&#xff0c;添加 r-tinymaix 为子模块到主仓库 ./sdk/packages/online-packages/r-tinymaix 路径下&#xff0c;命令如下所示&#xff1a; $ git subm…

java 手机商城免费搭建+电商源码+小程序+三级分销+SAAS云平台

【SAAS云平台】打造全行业全渠道全场景的SaaS产品&#xff0c;为店铺经营场景提供一体化解决方案&#xff1b;门店经营区域化、网店经营一体化&#xff0c;本地化、全方位、一站式服务&#xff0c;为多门店提供统一运营解决方案&#xff1b;提供丰富多样的营销玩法覆盖所有经营…

【数据结构/C++】栈和队列_链队列

#include <iostream> using namespace std; // 链队列 typedef int ElemType; typedef struct LinkNode {ElemType data;struct LinkNode *next; } LinkNode; typedef struct {LinkNode *front, *rear; } LinkQueue; // 初始化 void InitQueue(LinkQueue &Q) {Q.fron…

2024免费MacBook清理工具CleanMyMac X4.15

CleanMyMac X 是一款专业的Mac清理软件&#xff0c;可智能清理mac磁盘垃圾和多余语言安装包&#xff0c;快速释放电脑内存&#xff0c;轻松管理和升级 Mac 上的应用。同时 CleanMyMac X 可以强力卸载恶意软件&#xff0c;修复系统漏洞&#xff0c;一键扫描和优化 Mac 系统&…

医院手术麻醉信息系统全套源码,自主版权,支持二次开发

医院手术麻醉信息系统全套商业源码&#xff0c;自主版权&#xff0c;支持二次开发 手术麻醉信息系统是HIS产品的中的一个组成部分&#xff0c;主要应用于医院的麻醉科&#xff0c;属于电子病历类产品。医院麻醉监护的功能覆盖整个手术与麻醉的全过程&#xff0c;包括手术申请与…

Talk | 牛津大学博士后研究员边佳旺:SC-DepthV3-动态场景中的自监督单目深度估计

本期为TechBeat人工智能社区第550期线上Talk。 北京时间11月23日(周四)20:00&#xff0c;牛津大学博士后研究员—边佳旺的Talk已准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “SC-DepthV3&#xff1a;动态场景中的自监督单目深度估计”&#xff0c;介绍…

ros2文件package.xml与cmakelists.txt比较

每次在ros2里面添加文件以后&#xff0c;都要修改packages.xml,与cmakelists.txt文件。

ssm+vue的企业文档管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的企业文档管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…