草庐IT

【数据结构与算法】堆的实现(附源码)

sukuni 2023-04-13 原文

 

目录

一.堆的概念及结构

二.接口实现

A.初始化  Heapinit   销毁 Heapdestroy

B.插入 Heappush 向上调整  AdjustUp

1.Heappush

2.AdjustUp

C.删除 Heappop  向下调整  AdjustDown

D.堆的判空  Heapempty  堆顶数据  Heaptop  堆的大小  Heapsize

三.源码

Heap.h

Heap.c

test.c


一.堆的概念及结构

1.概念

     如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
2.堆的性质:
    A.堆中某个节点的值总是不大于或不小于其父节点的值
    B.堆总是一棵完全二叉树

其实堆是一种二叉树,通常我们都是用数据表实现,也就是说堆的底层是数组,数组中的小标表示二叉树的节点,所以在实现堆之前,我们有必要了解完全二叉树中节点之间的关系

1.理解父节点 parent 和子节点 child;

2.了解父节点与子节点之间的关系:

   A.parent=(child-1)/2;

   B.左孩子child=2*parent+1;

   C.右孩子child=2*parent+2。

二.接口实现

A.初始化  Heapinit   销毁 Heapdestroy

这里的初始化和销毁都很简单,相信这对学到堆的人并不是什么难事,和顺序表的操作是一样的,如果实在不理解的话,请看 ->  顺序表

B.插入 Heappush 向上调整  AdjustUp

1.Heappush

插入数据很简单,直接对数组赋值,然后 size 再加加就行了,但是在插入完数据后,我们得保证它是堆,所以这就需要用到向上调整这个函数。

2.AdjustUp

假设我们建的是大堆,我们将新插入的节点与它的父节点比较:

1.如果比它的父节点大,则与其交换,所以交换后的父节点就成为了子节点,再与其父节点比较,以此类推

2.如果child<=0 则结束循环

void Swap(HPdatatype* p1, HPdatatype* p2)  //交换函数
{
	HPdatatype tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustUp(HPdatatype* arr, int child)   //向上调整
{
	assert(arr);

	int parent = (child - 1) / 2;

	while (child > 0)
	{
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

void Heappush(Heap* php, HPdatatype x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		HPdatatype* tmp = (HPdatatype*)realloc(php->arr, 2 * sizeof(HPdatatype) * php->capacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}

		php->arr = tmp;
		php->capacity *= 2;
	}

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

	AdjustUp(php->arr, php->size - 1);  //注意这里要传size-1,因为size表示的是下一个位置
}

C.删除 Heappop  向下调整  AdjustDown

1.删除的话,我们是要删除堆顶的数据,因为删除堆尾的数据并没有什么实际意义,删除就是让size--,但是堆顶数据的下标是0,所以在删除前应先交换堆顶和堆尾的数据

2.删除完后,还要保持它还是个堆,不能把后面的顺序搞乱了,要想达到这个目的,就需要使用到向下调整这个函数;

3.假设是大堆,向下调整是父节点与其较大的子节点比较,如果较大的那个子节点大于父节点,则二者交换,然后较大的子节点成为了新的父节点,当子节点的下标大于或是等于节点总数,也就是size时,就结束循环。

 

D.堆的判空  Heapempty  堆顶数据  Heaptop  堆的大小  Heapsize

这些接口的实现都非常简单,博主就不在这里讲述了,可以参考后面的源码。


三.源码

Heap.h

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

#define MAX_MIN <   //通过改变这里的符号,可以决定是建大堆还是小堆

typedef int HPdatatype;

typedef struct Heap
{
	HPdatatype* arr;
	int size;
	int capacity;
}Heap;

void Heapinit(Heap* php);

void Swap(HPdatatype* p1, HPdatatype* p2);

void AdjustUp(HPdatatype* arr, int child);  //向上调整

void Heappush(Heap* php, HPdatatype x);

void AdjustDown(HPdatatype* arr, int parent, int n);  //向下调整

void Heappop(Heap* php);

HPdatatype Heaptop(Heap* php);

int Heapsize(Heap* php);

bool Heapempty(Heap* php);

void Heapdestroy(Heap* php);

Heap.c

#include "Heap.h"


void Heapinit(Heap* php)
{
	assert(php);

	php->arr = (HPdatatype*)malloc(sizeof(HPdatatype) * 4);
	if (php->arr == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	php->size = 0;
	php->capacity = 4;
}

void Swap(HPdatatype* p1, HPdatatype* p2)
{
	HPdatatype tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

///С

void AdjustUp(HPdatatype* arr, int child)
{
	assert(arr);

	int parent = (child - 1) / 2;

	while (child > 0)
	{
		if (arr[child] MAX_MIN arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

void Heappush(Heap* php, HPdatatype x)
{
	assert(php);

	if (php->size == php->capacity)   //插入前,判断数组是否已满
	{
		HPdatatype* tmp = (HPdatatype*)realloc(php->arr, 2 * sizeof(HPdatatype) * php->capacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}

		php->arr = tmp;
		php->capacity *= 2;
	}

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

	AdjustUp(php->arr, php->size - 1);  //这里要传size-1
}

void AdjustDown(HPdatatype* arr, int parent, int n)
{
	assert(arr);

	int child = 2 * parent + 1;

	while (child < n)
	{
        //判断较大(较小)的子节点
		if ((child + 1) < n && arr[child + 1] MAX_MIN arr[child])  
		{
			child++;
		}

		if (arr[child] MAX_MIN arr[parent])
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
			break;
	}
}

void Heappop(Heap* php)
{
	assert(php);
	assert(php->size);
	Swap(&php->arr[0], &php->arr[php->size - 1]);
	php->size--;

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

HPdatatype Heaptop(Heap* php)
{
	assert(php);
	assert(php->size);   //为空时不能取数据

	return php->arr[0];
}

int Heapsize(Heap* php)
{
	assert(php);

	return php->size;
}

bool Heapempty(Heap* php)
{
	assert(php);

	return php->size == 0;  //size==0即为空
}

void Heapdestroy(Heap* php)
{
	assert(php);
	free(php->arr);
	php->arr = NULL;
	php->size = 0;
	php->capacity = 0;
}

test.c

#include "Heap.h"


void testHeap()
{
	Heap hp;
	Heapinit(&hp);

	int i = 0, n = 10;
	int x = 0;
	while (n)
	{
		x = rand() % 100 + 1;

		Heappush(&hp, x);
		n--;
	}
	while (!Heapempty(&hp))
	{
		printf("%d  ", Heaptop(&hp));
		Heappop(&hp);
	}

	printf("\n");
	Heapdestroy(&hp);
}

int main()
{
	srand((unsigned int)time(NULL));
	testHeap();

	return 0;
}

🐲👻这循环队列的讲解就到这里了,若有错误或是建议欢迎小伙伴们指出。🐯🤖

🥰🤩希望小伙伴们可以多多支持博主哦。😍😃

😁😄谢谢你的阅读。😼😸

 

有关【数据结构与算法】堆的实现(附源码)的更多相关文章

  1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

    我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  5. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  6. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  7. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  8. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  9. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  10. UE4 源码阅读:从引擎启动到Receive Begin Play - 2

    一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame

随机推荐