DS堆的特性和实现(9)

news2024/12/27 1:08:09

文章目录

  • 前言
  • 一、堆的概念和结构
  • 二、堆的调整算法
    • 向下调整算法
    • 向上调整算法
    • 两种算法建堆的时间复杂度
  • 三、堆的实现
    • 结构体定义
    • 初始化和销毁
    • 堆的插入
    • 堆的删除
      • 挪移数据覆盖删除
      • 首尾交换再删除
    • 获取堆顶元素
    • 获取有效数据个数
    • 判断是否为空
  • 总结


前言

  继续,本篇较难
  正文开始!


一、堆的概念和结构

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

  额,这很严谨,有点高数概念的味道,我换种说法:
  堆就是以完全二叉树的顺序存储方式来存储元素,同时又要满足父亲结点存储数据都要大于等于儿子结点存储数据(也可以是父亲结点数据都要小于等于儿子结点数据)的一种数据结构。堆只有两种即大堆和小堆,大堆就是父亲结点数据大于等于儿子结点数据,小堆则反之

在这里插入图片描述

所以,其实我们可以得出堆的两点性质:

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

二、堆的调整算法

  以下代码部分是根据建小堆来走,如果需要建大堆可以修改直接的大于小于号

向下调整算法

 现在我们给出一个数组,逻辑上看作一棵完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆
在这里插入图片描述
但是要注意,这是有前提的!:

  1. 若想将其调整为小堆,那么根结点的左右子树必须都为小堆
  2. 若想将其调整为大堆,那么根结点的左右子树必须都为大堆

在这里插入图片描述
向下调整算法的基本思想(小堆):

  1. 从根结点处开始,选出左右孩子中值较小的孩子
  2. 让小的孩子与其父亲进行比较:若小的孩子比父亲还小,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止,若小的孩子比父亲大,则不需处理了,调整完成,整个树已经是小堆了
void Swap(HPDataType* a, HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

void AdjustDown(HPDataType* a, int size, int parent)
{
	//1.假设左孩子为小的数据
	int child = parent * 2 + 1;
	while (child < size)
	{
	//2.如果左孩子>右孩子 则将右孩子赋值
	//有可能只有左孩子 所以加条件
	//以下未有左右孩子且左孩子>右孩子情况,则将child++
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;
		}
		//3.将孩子与父亲进行比较 如果孩子小则交换
		//然后将父亲和孩子移动到下一个位置
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

向上调整算法

 当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法

在这里插入图片描述
向上调整算法的基本思想(小堆):

  1. 将目标结点与其父结点比较
  2. 若目标结点的值比其父结点的值小,则交换目标结点与其父结点的位置,并将原目标结点的父结点当作新的目标结点继续进行向上调整。若目标结点的值比其父结点的值大,则停止向上调整,此时该树已经是小堆了

在这里插入图片描述
同样的,向上调整算法也有一个前提:

  1. 若想将其调整为小堆,那么原来的数据为小堆。
  2. 若想将其调整为大堆,那么原来的数据为大堆。
void Swap(HPDataType* a, HPDataType* b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0) // 请思考一下是child > 0
	{
		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

两种算法建堆的时间复杂度

一、向下调整算法
我们只需要从倒数第一个非叶子结点开始,从后往前,按下标,依次作为根去向下调整即可
在这里插入图片描述

所以向下调整算法的时间复杂度为O(N)

二、向上调整算法
这时候我们就需要从第二个节点到最后一个节点,依次向上调整建堆了
在这里插入图片描述

所以向上调整算法的时间复杂度为O(NlogN)

因此,对于任意的一维数组(逻辑上看作是树),我们同一采用向下调整算法,从倒数第一个非叶子节点开始,因为它的时间复杂度比较低!

三、堆的实现

  在有了前文向上调整向下调整这两个利器之后,我们就可以来实现堆了

结构体定义

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;//存放数据的动态数组
	int size;     //有效数据个数
	int capacity; //数组容量
}HP;

初始化和销毁

void HeapInit(HP* php)
{
	assert(php);//断言避免出现空指针
	php->a = NULL;
	php->capacity = php->size = 0;
}

void HeapDestory(HP* php)
{
	assert(php);
	free(php->a);//释放动态数组
	php->size = php->capacity = 0;
	
	free(php);
	php = NULL;
}

堆的插入

 思路是先将新数据插入到一维数组(逻辑上是堆)的末尾,因为未插入之前是堆,插入后要还是堆,我们之间向上调整即可
在这里插入图片描述

void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	//1.检查容量
	if (php->size == php->capacity)
	{
		int newCapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newCapacity;
	}
	//2.插入数据
	php->a[php->size] = x;
	php->size++;

	//3.调整数据
	AdjustUp(php->a, php->size-1);
}

堆的删除

 关于堆的删除,我们一般默认规定删除堆顶也是就是根节点,至于删除尾部数据意义不大,因为尾部数据没有特别的地方,既不是最大,也不是最小

挪移数据覆盖删除

 挪移数据覆盖会导致堆发生严重BUG,整棵树的父子关系全乱,也就是需要维持大小关系乱了

倒反天罡!

在这里插入图片描述

首尾交换再删除

 对于堆的删除,我们采用另外一种方法,首尾交换再删除,左右子树依旧是堆,同时关系也没有乱,并且删除堆顶数据通过尾删再向下调整代价很低

 思路也简单,就是先交换头尾元素,然后再size自减,最后头节点向下调整
在这里插入图片描述

void HeapPop(HP* php)
{
	assert(php);
	//有数据才删除
	assert(php->size > 0);
	//1.将首位数据交换
	Swap(&php->a[0], &php->a[php->size - 1]);
	//2.删除尾数据
	php->size--;
	//3.向下调整
	AdjustDown(php->a, php->size, 0);
}

获取堆顶元素

这很简单了,直接返回根节点数据即可

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

获取有效数据个数

int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

判断是否为空

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

总结

  哈哈,初步感受到堆的巧妙了吧,我们后面继续来学习堆的实际应用!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我的创作纪念日-365天的感悟

时光荏苒&#xff0c;岁月如梭。转眼间&#xff0c;自己在CSDN注册已经整整15个年头了。回想起当初&#xff0c;还是个满怀憧憬、对未来充满无限好奇的学生哥。如今&#xff0c;虽然身份和角色发生了诸多变化&#xff0c;但CSDN始终陪伴着我&#xff0c;见证了我的成长与蜕变。…

JavaWeb环境下的Spring Boot在线考试系统开发

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理基于JavaWeb技术的在线考试系统设计与实现…

深入探讨C++多线程性能优化

深入探讨C多线程性能优化 在现代软件开发中&#xff0c;多线程编程已成为提升应用程序性能和响应速度的关键技术之一。尤其在C领域&#xff0c;多线程编程不仅能充分利用多核处理器的优势&#xff0c;还能显著提高计算密集型任务的效率。然而&#xff0c;多线程编程也带来了诸…

OpenAI的新功能Canvas,效果还不错

时隔两年&#xff0c;ChatGPT终迎来界面全新升级&#xff01; 这一次&#xff0c;OpenAI官宣推出类似 Anthropic 的 Artifacts 的界面交互功能 canvas&#xff0c;并称这是一种使用 ChatGPT 写作和编程的新方式。不论是写作&#xff0c;还是编码&#xff0c;都可以开启全新的交…

什么!我上传的文件不见了?

什么&#xff01;我上传的文件不见了? 前言&#xff1a; 最近在实现一个文件上传功能时使用了异步处理&#xff0c;但是在异步处理文件时&#xff0c;却提示NoSuchFileException错误。简化代码如下&#xff1a; PostMapping("/upload")void testFileUpload(Reques…

Flume抽取数据(包含自定义拦截器和时间戳拦截器)

flume参考网址&#xff1a;Flume 1.9用户手册中文版 — 可能是目前翻译最完整的版本了https://flume.liyifeng.org/?flagfromDoc#要求&#xff1a; 使用Flume将日志抽取到hdfs上&#xff1a;通过java代码编写一个拦截器&#xff0c;将日志中不是json数据的数据过滤掉&#xf…

学习文档10/16

MySQL 字符集&#xff1a; MySQL 支持很多种字符集的方式&#xff0c;比如 GB2312、GBK、BIG5、多种 Unicode 字符集&#xff08;UTF-8 编码、UTF-16 编码、UCS-2 编码、UTF-32 编码等等&#xff09;。 查看支持的字符集 你可以通过 SHOW CHARSET 命令来查看&#xff0c;支持…

一次性理清Environment体系

在Spring中&#xff0c;我们可以通过配置文件等方式去进行一些属性值的配置&#xff0c;比如通过Value注解去获取到对应的属性值&#xff0c;又或者说是想在程序运行时获取系统环境变量&#xff0c;类似的这些操作其实都是去获取一些配置数据&#xff0c;所以在Spring中对这些数…

C++ | Leetcode C++题解之第486题预测赢家

题目&#xff1a; 题解&#xff1a; class Solution { public:bool PredictTheWinner(vector<int>& nums) {int length nums.size();auto dp vector<int>(length);for (int i 0; i < length; i) {dp[i] nums[i];}for (int i length - 2; i > 0; i-…

基于SpringBoot+Vue+uniapp的在线招聘平台的详细设计和实现

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

喜报丨财富通科技荣获ISO9001、ISO27001及ISO20000三项国际认证

近日&#xff0c;财富通科技成功通过ISO9001、ISO27001及ISO20000三项国际标准认证。这标志着公司在质量管理体系、信息安全管理体系以及信息技术服务管理体系建设方面达到了国际认可的标准。 ISO9001认证表明财富通科技在软件开发、技术服务和项目管理等方面建立了一套完善的…

带你学习如何编写一篇API详设文档以及给新人提点建议

文章目录 前言先认清一个问题详设文档如何写先看文档脉络详设文档分析需求背景方案概述API定义安全设计性能设计缓存与数据库 总结 前言 这篇文章带读者了解软件开发项目中一个需求的开发详设文档主要包括哪些内容&#xff0c;其中重点会给读者分析API设计的规范&#xff0c;相…

推荐算法的学习

文章目录 前言1、模型1.1 从本领域模型的发展历史中学习1.1.1 在历史中总结发展规律和趋势1.1.2 发现模型之间的共性&#xff0c;方便记忆 1.2 从其他领域的发展中学习1.2.1 注意力机制1.2.2 残差网络 1.3 实践该怎么办&#xff1f; 2、 特征2.1 数据源的选择与建立2.2 特征构造…

react18中实现简易增删改查useReducer搭配useContext的高级用法

useReducer和useContext前面有单独介绍过&#xff0c;上手不难&#xff0c;现在我们把这两个api结合起来使用&#xff0c;该怎么用&#xff1f;还是结合之前的简易增删改查的demo&#xff0c;熟悉vue的应该可以看出&#xff0c;useReducer类似于vuex&#xff0c;useContext类似…

AirServer2024你的手机投屏神器,轻松实现多屏互动!

&#x1f4a1;**开篇点题**&#x1f4a1; 说起现代科技的魔力&#xff0c;小伙伴们是否还记得那个让你在公司会议、家庭影院乃至游戏战场上都能大显身手的神奇软件——AirServer&#xff1f;没错&#xff0c;就是那个让你手机秒变超级大屏的投屏神器&#xff01;今天我要和大家…

WebGIS开发系列教程

WebGIS开发-00保姆级、零基础入门教程 WebGIS开发-01开发环境搭建 WebGIS开发-02vite搭建htmlcssjs开发框架 WebGIS开发-03在框架中引入地图 WebGIS开发-04.搭建Vue3jsscss框架开启编程之旅 B Zhan持续更新中....

机器学习数据标准化与归一化:提升模型精度的关键

&#x1f4d8;数据标准化与归一化&#xff1a;提升模型精度的关键 机器学习中的数据处理环节至关重要&#xff0c;其中&#xff0c;数据标准化与归一化是提高模型性能的关键步骤之一。数据的特征尺度往往不一致&#xff0c;直接影响模型的训练效果&#xff0c;因此对数据进行处…

用sdkman管理多个jdk切换

前言 最近项目前后端进行升级&#xff0c;需要在jdk8和jdk17两个版本切换。最简单的是通过手动切换&#xff0c;但切换过程太繁琐&#xff0c;修改环境变量&#xff0c;达到切换目的。于是尝试其它解决方案&#xff0c;最终确实使用sdkman工具。 sdkman 是一款面向Java开发者的…

十分钟掌握Ajax(jQuery封装的ajax)

Ajax是一种异步&#xff08;无需等待服务器返回数据就可以做别的工作&#xff09;无刷新&#xff08;做了一些操作之后&#xff0c;页面不会刷新&#xff09;技术&#xff0c;通常结合DOM一起操作。(不像超链接和表单一样一点就刷新) Jquery封装好的Ajax技术有四种&#xff0c…

苹果开源Depth Pro:0.3秒实现从2D图像到3D深度图的革命性突破

前沿科技速递&#x1f680; 近日&#xff0c;苹果公司的AI研究团队震撼推出了一项划时代的技术——Depth Pro。这一技术能够在0.3秒内从单一的2D图像中生成高精度的3D深度图&#xff0c;突破了单目深度估计技术的极限。这项创新将为智能设备和计算机视觉领域带来全新的应用可能…