数据结构::堆

news2024/12/24 17:18:40

堆的定义及解释

专业定义:

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


太复杂了,我们通俗一点理解。
一句话解释,堆是一颗完全二叉树,且堆只有大堆小堆

大堆:堆的每一个节点都比子节点大
小堆:堆的每一个节点都比子节点小


小堆:
在这里插入图片描述


大堆:
在这里插入图片描述


一道练习题,看看有没有掌握堆的定义

1.下列关键字序列为堆的是:()
A 100,60,70,50,32,65
B 60,70,65,50,32,100
C 65,100,70,32,50,60
D 70,65,100,32,50,60
E 32,50,100,70,65,60
F 50,100,70,65,60,32

答案:A


堆的实现

堆既可以基于顺序结构实现,也可以基于链式结构实现。
但是,毕竟堆是一颗完全二叉树,根据完全二叉树的性质,这里采用顺序结构实现更加的简单

ps:完全二叉树的性质:

下标之间存在固定的关系
leftchild = parent * 2 + 1
leftchild = parent * 2 + 2
parent = (child - 1) / 2 --------//这里左右子节点下标通用

不清楚的同学可以下去自己画个图验证一下。


C++官方库主要实现了以下功能

在这里插入图片描述
这里是拿的priority_queue的成员函数,stl中priority_queue就是一个堆。
stl中priority使用了仿函数来实现了大小堆的自由切换,我们这里为了简单,就以大堆为例,库里面默认也是大堆。
感兴趣的同学,可以使用仿函数模拟实现一下库里面的priority_queue。

此外,库里面priority_queue采用了适配器的设计模式来实现,固然这更加的简单,高效,但是我们还是应该学会堆的原理,万一面试的时候,面试官让我们手撕一个堆,如果不会的话,面试直接就挂了。


首先,来看一下堆的成员

template<class T>
class Heap
{
private:
	T* _heap;
	size_t _size;
	size_t _capacity;
public:
//构造析构
	Heap(size_t size = 0, size_t capacity = 0);
	~Heap();

//插入删除
    void push(const T& val);
    void pop();

//获取堆顶元素
   T& top();

//size和empty
   size_t size();
   bool empty();

这里主要还是实现插入删除这两个函数,其余函数没有技术含量,等会直接看代码就行。


push函数

注:本博客以大堆为例。

单纯push非常的简单,难在哪里呢?
问题在于,我们在push之后,还需要保持数据结构依然是一个堆,要维护好堆的性质。

这里引入向上调整算法,AdjustUp()

基本原理就是,用子节点与父节点的数据相比较:
如果子节点更大,交换子节点和父节点的数据,维护一下下标,继续向上调整,知道子节点的下标到0;
如果父节点更大,就符合大堆的性质,直接不用继续比较了。

void AdjustUp(size_t child)
{
	size_t parent = (child - 1) / 2;

	while (child > 0)
	{
		if (_heap[parent] < _heap[child])
		{
			std::swap(_heap[parent], _heap[child]);

			child = parent;
			parent = (parent - 1) / 2;
		}
		else break;
	}
}

插入的时候,先检查一下容量够不够,不够就扩容,然后尾插数据,接着size++,在使用AdjustUp维护好堆即可。

void push(const T& val)
{
	if (_size == _capacity)//扩容
	{
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
		T* tmp = new T[newcapacity];

		if(_heap)
		for (size_t i = 0; i < newcapacity; i++)
		{
			tmp[i] = _heap[i];
		}

		_heap = tmp;
		_capacity = newcapacity;
	}

	_heap[_size] = val;
	_size++;

	AdjustUp(_size - 1);
}

pop函数

pop函数的实现思路比push函数更加有意思。
堆的pop函数的作用是删除堆顶元素,不是尾删,不要闹出笑话,哈哈哈。

pop函数的思路是:
将堆顶元素与最后一个元素交换,size–
再采用AdjustDown算法维护堆

这个思路是非常巧妙的,要深刻体会一下,博主能力有限,没想出来很好的解释方法,对不住读者。
读者在读到这里的时候,不妨与自己的思路进行一下比较,体会一下这个思路的巧妙之处。

AdjustDown算法思路:
用父亲的下标确定子节点的下标,
选取两个节点中更大的那一个(如果有两个子节点的话)
父节点与该更大的子节点进行比较,
如果子节点更大,交换父子节点,并重新继续向下比较
如果父节点更大,结束算法

AdjustDown算法有几处细节需要注意:
a、只有两个子节点都存在时,才需要比较两个节点,有可能在最后一个叶子节点的时候,只有一个子节点
b、结束循环的条件,一个是父节点更大,另一个是,子节点的下标 >= size

//AdjustDown算法
void AdjustDown(size_t parent)
{
	size_t child = parent * 2 + 1;

	while (child < _size)
	{
		if (child + 1 < _size && _heap[child] < _heap[child + 1])
		{
			child++;
		}

		if (_heap[parent] < _heap[child])
		{
			std::swap(_heap[parent], _heap[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else break;
	}
}
//pop函数
void pop()
{
	assert(_size > 0);
	std::swap(_heap[0], _heap[_size - 1]);
	_size--;//先删除,再向下调整
	AdjustDown(0);
}

整体代码(仅供参考)

template<class T>
class Heap
{
private:
	T* _heap;
	size_t _size;
	size_t _capacity;
public:
	Heap(size_t size = 0, size_t capacity = 0)
		:_size(size),_capacity(capacity)
	{
		_heap = nullptr;
	}

	~Heap()
	{
		delete[] _heap;
		_size = _capacity = 0;
	}

	void AdjustUp(size_t child)
	{
		size_t parent = (child - 1) / 2;

		while (child > 0)
		{
			if (_heap[parent] < _heap[child])
			{
				std::swap(_heap[parent], _heap[child]);

				child = parent;
				parent = (parent - 1) / 2;
			}
			else break;
		}
	}

	void AdjustDown(size_t parent)
	{
		size_t child = parent * 2 + 1;

		while (child < _size)
		{
			if (child + 1 < _size && _heap[child] < _heap[child + 1])
			{
				child++;
			}

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

	void push(const T& val)
	{
		if (_size == _capacity)//扩容
		{
			size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
			T* tmp = new T[newcapacity];

			if(_heap)
			for (size_t i = 0; i < newcapacity; i++)
			{
				tmp[i] = _heap[i];
			}

			_heap = tmp;
			_capacity = newcapacity;
		}

		_heap[_size] = val;
		_size++;

		AdjustUp(_size - 1);
	}

	void pop()
	{
		assert(_size > 0);
		std::swap(_heap[0], _heap[_size - 1]);
		_size--;//先删除,再向下调整
		AdjustDown(0);
	}

	T& top()
	{
		return _heap[0];
	}

	size_t size()
	{
		return _size;
	}

	bool empty()
	{
		return _size == 0;
	}
};

运行效果

测试代码:
在这里插入图片描述

运行结果:
在这里插入图片描述


提前祝大家中秋节快乐!!!

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

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

相关文章

Linux内核编译并移植至ARM平台

Linux内核编译并移植至ARM平台 Linux系统相关概念操作系统的启动过程获取树莓派源码Linux内核源代码目录树结构 树莓派Linux源码配置树莓派Linux内核编译移植内核至树莓派 Linux系统相关概念 操作系统的启动过程 x86&#xff0c;Intel的启动过程&#xff1a; 电源上电 -> B…

基于scRNA-data,运用pySCENIC寻找细胞群里面活跃的调节子

愿武艺晴小朋友一定得每天都开心 pySCENIC 在步骤上&#xff0c;分为4大步&#xff1a; 1&#xff09;准备工作&#xff1a;counts矩阵文件&#xff08;R语言写出csv文件&#xff09; 2&#xff09;将csv文件转换为loom文件(用python做) 3&#xff09;然后slurm平台申请资源…

概述03 A/B test

分层实验&#xff1a;

Python 课程9-資料庫操作

前言 在现代软件开发中&#xff0c;数据库是核心组件之一&#xff0c;它负责数据的存储、管理和检索。无论是简单的应用程序还是复杂的企业级系统&#xff0c;数据库操作都是必不可少的。本教程将深入讲解如何使用 Python 进行数据库操作&#xff0c;涵盖使用 sqlite3 进行本地…

Autosar学习----AUTOSAR_SWS_BSWGeneral(四)

General Implementation Specification 7.1 General Implementation Specification7.1.1 遵循 MISRA C 和 C 标准7.1.2 遵循 AUTOSAR 基本软件需求7.1.3 遵循 AUTOSAR 方法论7.1.4 平台独立性和编译器抽象7.1.5 可配置性7.1.6 各种命名约定7.1.7 配置参数7.1.8 共享代码7.1.9 全…

论文写作难?用这套ChatGPT提示词3小时完成论文初稿!

AIPaperGPT&#xff0c;论文写作神器~ https://www.aipapergpt.com/ 还在因为写论文头秃吗&#xff1f;你不是一个人&#xff01;从选题到文献综述&#xff0c;再到最后的结论&#xff0c;每一步都让人抓狂。选题纠结、文献多到看不过来、结构怎么写都不满意——这些是不是让你…

AI 加持的云端 IDE——三种方法高效开发前后端聊天交互功能

以下是「豆包 MarsCode 体验官」优秀文章&#xff0c;作者努力的小雨。 豆包 MarsCode 豆包MarsCode 编程助手支持的 IDE: 支持 Visual Studio Code 1.67.0 及以上版本&#xff0c;以及 JetBrains 系列 IDE&#xff0c;如 IntelliJ IDEA、Pycharm 等&#xff0c;版本要求为 22…

C++八股文基础知识点

1.指针和引用的区别 指针是一个指向内存地址的变量&#xff0c;其本身是一个地址&#xff0c;地址保存的是变量的值&#xff0c;而且它本身可变&#xff0c;包括它指向的地址和地址上的存放的数据&#xff1b;引用即为一个变量的地址&#xff0c;也是变量的别名&#xff0c;和…

opeGauss 之BitmapOr算子代码走读

一. 前言 在openGauss中&#xff0c;BitmapOr算子扫描是指谓词条件是索引列的or条件时&#xff0c;将多个or条件的索引谓词先组成一个需要扫描元组的BitmapOr扫描算子&#xff0c;然后再通过BitmapOr算子的tid信息去扫描元组&#xff0c;从而减少or条件带来的随机扫描过多的问题…

《企业实战分享 · SonarQube10.x 详细教程》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

线下活动|落地武汉、长沙高校,10场AI沙龙火爆来袭

人工智能作为引领时代发展的重要力量&#xff0c;正以前所未有的速度改变着我们的世界&#xff0c;而智能体因其开发门槛低、应用场景广泛等优势成为最热门的AI应用方向之一&#xff0c;未来将拥有庞大生态。为此&#xff0c;百度百科校园在开学季的9月联合文心智能体平台在武汉…

清理C盘缓存的垃圾,专业清理C盘缓存垃圾的步骤与策略

在维护计算机系统的过程中&#xff0c;定期清理C盘&#xff08;通常是系统盘&#xff09;中的缓存和垃圾文件是一项至关重要的任务。这不仅能有效释放磁盘空间&#xff0c;提升系统性能&#xff0c;还能减少因磁盘空间不足导致的程序运行缓慢或错误。以下是一系列专业且安全的步…

品胜电子携手成都蓉城,共谋“商业+体育”合作新篇章

9月14日,广东品胜电子股份有限公司(以下简称品胜电子)与成都蓉城足球俱乐部(以下简称成都蓉城)正式签约,成为其官方合作伙伴。全国数码3C行业领军企业与中超强队的携手合作,标志着双方将在“商业体育”融合的模式下资源共享相互赋能,提升其品牌影响力。 蓉城足球俱乐部相关人士…

HTB-Included(本地文件包含、TFTP文件上传、LXD容器挂载目录)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解Included靶机 渗透过程 信息搜集 rustscan工具 通常只扫描TCP端口&#xff0c;所以我们要配合nmap进行-sU 执行udp端口扫描 可以观察到 服务器开放了TFTP UDP端口&#xff0c;用来进行文件传输协议…

2024年度10款视频剪辑软件分享

在视频创作日益盛行的今天&#xff0c;一款强大而易于上手的视频剪辑软件成为了每位创作者不可或缺的工具。无论是专业电影制作人、Vlog博主&#xff0c;还是视频编辑爱好者&#xff0c;都能在市场上找到适合自己的那一款。以下是2024年度推荐的10款视频剪辑软件&#xff0c;涵…

推荐一款免费的图片压缩软件,自媒体运营者、摄影爱好者的必备工具

随着社会的发展&#xff0c;咱们生活里的各种新鲜玩意儿越来越多&#xff0c;互联网就像是一个超级大的市场&#xff0c;啥都能干。比如说&#xff0c;咱们现在拍照、看视频、刷朋友圈、逛网店&#xff0c;这些都离不开图片。但是呢&#xff0c;图片这玩意儿&#xff0c;它占地…

self-play RL学习笔记

让AI用随机的路径尝试新的任务&#xff0c;如果效果超预期&#xff0c;那就更新神经网络的权重&#xff0c;使得AI记住多使用这个成功的事件&#xff0c;再开始下一次的尝试。——llya Sutskever 这两天炸裂朋友圈的OpenAI草莓大模型o1和此前代码能力大幅升级的Claude 3.5&…

跨平台Markdown写作软件:小书匠

本文软件由好友 Eduna 推荐&#xff1b; 什么是小书匠 &#xff1f; 小书匠是一款本地优先&#xff0c;去中心化&#xff0c;分布式&#xff0c;支持选择性同步的全平台覆盖笔记软件&#xff0c;是专注于 markdown 写作的客户端编辑器。它支持多种平台&#xff0c;包括 Windows…

Qt Model/View之代理

概念 与模型-视图-控制器模式不同&#xff0c;模型/视图设计没有包含一个完全独立的组件来管理与用户的交互。通常&#xff0c;视图负责向用户展示模型数据&#xff0c;并负责处理用户输入。为了在获取输入的方式上具有一定的灵活性&#xff0c;交互由委托执行。这些组件提供输…

5 个最佳开源无代码项目管理工具

想象一下&#xff0c;你是一名项目经理&#xff0c;每天早上刚坐到办公桌前&#xff0c;咖啡还在手里&#xff0c;你的手机和电脑屏幕上已经被各种未完成的任务、项目更新和团队消息填满。 你快速浏览着的电子邮件&#xff0c;分配任务的通知不断弹出&#xff0c;而每一个通知…