【C++】优先级队列(容器适配器)

news2024/9/20 7:50:09

欢迎来到我的Blog,点击关注哦💕

前言

string vector list 这种线性结构是最基础的存储结构,C++(STL)container很好的帮助我们数据存储的问题。

容器适配器

介绍

  • 容器适配器是C++标准模板库(STL)中的一种设计模式,它允许将一个容器的接口转换为另一个接口,从而提供不同的操作和行为。
  • 容器适配器通常用于封装现有容器,以实现特定的数据结构特性,如栈(后进先出)、队列(先进先出)和优先队列(根据优先级排序)。

应用

  • 栈(stack):栈是一种后进先出的数据结构,其操作包括入栈(push)、出栈(pop)、查看栈顶元素(top)等。栈适配器可以基于多种底层容器实现,如vectordequelist.

  • 队列(queue):队列是一种先进先出的数据结构,其操作包括入队(push)、出队(pop)、查看队首元素(front)和查看队尾元素(back)。队列适配器同样可以基于dequelist实现,以适应不同的性能需求.

  • 优先队列(priority_queue):优先队列是一种特殊的队列,它根据元素的优先级进行排序。其底层容器通常是vectordeque,并通过堆算法维护元素的优先级顺序。优先队列适配器提供了插入和删除具有最高优先级元素的操作.

双重结束队列(双端队列(deque))

特点

  • 双端操作效率:支持在两端进行快速的插入和删除操作。
  • 随机访问:可以通过索引直接访问容器中的元素。
  • 无需预先分配固定大小:与vector不同,deque不需要在创建时指定大小,它可以根据需要动态增长。
  • 内存分配策略deque不需要像vector那样一次性分配大量内存,而是分散在内存中,这有助于减少内存碎片。

存储结构

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落 在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Listvector deque对比

对比维度VectorDequeList
内存连续性
随机访问性能O(1)O(1) 但可能不如VectorO(n)
插入/删除性能非末尾O(n)两端O(1), 中间O(n)两端及中间O(1)
内存重用效率扩容时需移动元素两端添加删除不需移动不适用
内存分配模式动态数组,连续内存分段连续内存非连续内存
迭代器失效可能不会不会
支持的操作[] 访问、.at() 等[] 访问、.at() 等[] 访问、.at() 等
内存管理开销高(扩容时)中等(两端操作)
适用场景需要快速随机访问且元素数量稳定需要两端快速插入删除,随机访问需求适中频繁插入删除,不关心随机访问

栈(stack)

栈的介绍

函数说明接口说明
stack()构造空的栈
empty()检测stack是否为空
size()返回stack中元素的个数
top()返回栈顶元素的引用
push()将元素val压入stack中
pop()将stack中尾部的元素弹出

栈的模拟实现

利用容器适配器的设计原理,很容易实现

  • 将栈放mystack的命名空间,以防止和库中冲突
  • 类模板设计container可以给缺省参数,默认deque(容器适配器)
  • 在里面利用deque的接口实现
namespace mystack
{
	template<class T, class Container = std::deque<T>>
	class stack
	{
	public:

		void push_back(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		size_t size()
		{
			return _con.size();
		}
		T& top()
		{
			return _con.back();
		}

		bool empty()
		{
			return _con.empty();
		}
	private:

		Container _con;
	};
}

队列

队列介绍

函数声明接口说明
queue()构造空的队列
empty()检测队列是否为空,是返回true,否则返回false
size()返回队列中有效元素的个数
front()返回队头元素的引用
back()返回队尾元素的引用
push()在队尾将元素val入队列
pop()将队头元素出队列

队列模拟实现

  • 将栈放myqueue的命名空间,以防止和库中冲突
  • 类模板设计container可以给缺省参数,默认deque(容器适配器)
  • 在里面利用deque的接口实现
namespace myqueue
{
	template<class T, class Container = std::deque<T >>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();
		}
		size_t size()
		{
			return _con.size();
		}
		T& front()
		{
			return _con.front();
		}

		bool empty()
		{
			return _con.empty();
		}
	private:

		Container _con;
	};
}

优先级队列(priority_queue)

基本原理

  • 优先级队列通常在内部使用堆数据结构来维护元素的优先级。
  • 堆是一种完全二叉树,可以是最大堆或最小堆。
  • 在最大堆中,父节点的值总是大于或等于其子节点的值,而在最小堆中,父节点的值总是小于或等于其子节点的值。
  • 插入操作通过在堆的适当位置插入新元素并进行上调整(heapify-up)来维持堆的性质。
  • 删除操作则涉及到移除堆顶元素(优先级最高的元素)并进行下调整(heapify-down),以恢复堆的结构。

priority_queue介绍

函数声明接口说明
priority_queue()/priority_queue(first, last)构造一个空的优先级队列
empty( )检测优先级队列是否为空,是返回true,否则返回 false
top( )返回优先级队列中最大(最小元素),即堆顶元素
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

优先级模拟实现 (可以参考)

仿函数

  • 仿函数(Functor)是C++中的一个编程概念,它指的是一个类或结构体,通过重载函数调用运算符operator(),使得这个类或结构体的对象可以像函数一样被调用。
  • 仿函数可以包含状态,因为它们是对象,可以在构造函数中初始化状态,并在operator()中使用该状态。
  • 仿函数可以作为参数传递给其他函数,包括STL算法中的函数,从而提供灵活的编程模型.

这个就是一个仿函数

//小于
template<class T>
	class Less
	{
	public:
		bool operator()(const T& a,const T& b)
		{
			return a < b;
		}
	};
//大于
template<class T>
	class Greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

priority_queue两个关键

向下建堆

  • 确定起始点:从最后一个非叶子节点开始向下建堆,这个节点也被称为堆的最后一个非叶子节点。在完全二叉树中,最后一个非叶子节点的索引可以通过 (n - 1 - 1) / 2 计算得到,其中 n 是数组的长度。
  • 执行向下调整:对每个非叶子节点执行向下调整操作,确保该节点与其子节点组成的子树满足堆的性质。向下调整的过程涉及到与子节点的比较和必要时的交换,直至到达堆的顶部或直到父节点不再违反堆的性质。
  • 迭代过程:从最后一个非叶子节点开始,逐步向上调整,直到根节点。每次调整后,更新当前节点的索引,以便进行下一次调整。
  • 完成建堆:重复步骤2和步骤3,直到根节点也满足堆的性质,此时整个数组就构建成了一个堆。
void AdjustDown(size_t parent)
{
    compare com;//仿函数

    size_t child = parent * 2 + 1;
    //if (child+1< _con.size() && _con[child] < _con[1+child])
    if (child + 1 < _con.size() && com(_con[child] ,_con[1 + child]))//和上面等价

    {
        ++child;
    }
    while (child <_con.size())
    {
        if (com(_con[parent], _con[child]))
        {
            std::swap(_con[child], _con[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

向上建堆

  • 初始化堆大小**:设置堆的大小为数组的大小,即 n
  • 从最后一个非叶子节点开始向上调整:在完全二叉树中,最后一个非叶子节点的索引为 floor((n - 1) / 2)。从这个节点开始向上调整,确保每个节点都满足大根堆的性质。
  • 执行向上调整操作:对于每个非叶子节点,检查其与子节点的关系,并进行必要的交换,以确保父节点的值大于或等于其子节点的值。如果子节点中有一个或两个,选择较大的子节点与父节点进行比较。如果父节点的值小于子节点的值,交换它们的位置,并重新设置父节点为当前子节点,继续向上调整。
  • 重复步骤2和3:直到达到根节点,即堆的第一个元素。
void AdjustUp(int child)
{
    compare com;

    int parent = (child - 1) / 2;
    while (child > 0)
    {
        if (com(_con[parent] , _con[child]))
        {
            std::swap(_con[parent], _con[child]);
            child = parent;
            parent = (child - 1) / 2;
        }
        else
        {
            break;
        }
    }

}

初始化数据

迭代器初始化

  • 模板嵌套给,迭代器初始化

  • 依次push数据,在进行堆的建立

template<class InputIterator>
void push_back(InputIterator first, InputIterator last)
	{
		while (first != last)
		{
			_con.push_back(*first);
			++first;
		}

		//向下建堆
		for (int i = (_con.size() - 2) / 2; i >= 0; i--)
		{
			AdjustDown(i);
		}
	}

pop数据

  • 将一个个数据和最后一个数据进行交换(目的:保持当前堆的结构)
  • pop出数据,将第一个数据进行向下调整
void pop()
{
	swap(_con[0], _con[_con.size() - 1]);
		
	_con.pop_back();

	AdjustDown(0);
}

push数据

  • 将数据进行尾插入,进行向上调整
void push(const T& x)
{
    _con.push_back(x);

    AdjustUp(_con.size() - 1);
}

priority_queue operators

  • top数据,返回首个数据;
  • 其他常见操作,采取容器适配器设计模式的操作
namespace mypriority_queue
{
	template<class T, class container = std::vector<T>,class compare = Less<T>>

	class priority_queue
	{
	public:
		const T& top()
		{
			return _con[0];
		}
		size_t szie()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}

	private:
		container _con;
	};

}

源码(优先级队列)

namespace mypriority_queue
{
	template<class T>
	class Less
	{
	public:
		bool operator()(const T& a,const T& b)
		{
			return a < b;
		}
	};

	template<class T>
	class Greater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

	template<class T, class container = std::vector<T>,class compare = Less<T>>

	class priority_queue
	{


		void AdjustDown(size_t parent)
		{
			compare com;

			size_t child = parent * 2 + 1;
			//if (child+1< _con.size() && _con[child] < _con[1+child])
			if (child + 1 < _con.size() && com(_con[child] ,_con[1 + child]))

			{
				++child;
			}
			while (child <_con.size())
			{
				if (com(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void AdjustUp(int child)
		{
			compare com;

			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent] , _con[child]))
				{
					std::swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		
		}

	public:
		template<class InputIterator>
		void push_back(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}

			//向下建堆
			for (int i = (_con.size() - 2) / 2; i >= 0; i--)
			{
				AdjustDown(i);
			}
		}

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			
			_con.pop_back();

			AdjustDown(0);
		}

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

		void push(const T& x)
		{
			_con.push_back(x);

			AdjustUp(_con.size() - 1);
		}
		size_t szie()
		{
			return _con.size();
		}

		bool empty()
		{
			return _con.empty();
		}

	private:
		container _con;
	};

}

向下建堆
for (int i = (_con.size() - 2) / 2; i >= 0; i–)
{
AdjustDown(i);
}
}

	void pop()
	{
		swap(_con[0], _con[_con.size() - 1]);
		
		_con.pop_back();

		AdjustDown(0);
	}

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

	void push(const T& x)
	{
		_con.push_back(x);

		AdjustUp(_con.size() - 1);
	}
	size_t szie()
	{
		return _con.size();
	}

	bool empty()
	{
		return _con.empty();
	}

private:
	container _con;
};

}


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

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

相关文章

sqllabs游戏

文章目录 总体思路&#xff1a;less-1:less-2:less-3:less-4:less-5:less-6:less-7:less-8:布尔盲注less-9:时间盲注less-21:less-24: 总体思路&#xff1a; 1、第一件事情 逃脱出单引号的控制 闭合单引号 2、单双引号需要成对出现 在python php Java中 3、2个办法 继续把多出…

linux文件命令:更新文件时间戳的工具touch详解

目录 一、概述 二. touch 命令的基本用途 三. touch 命令的语法 3.1、语法 3.2、touch 命令的选项 3.3、时间字符串格式 四. 常用场景 4.1 创建空文件 4.2 同时创建多个文件 4.3 更新文件的时间戳 4.4 只更新访问时间 4.5 只更新修改时间 4.6 设置特定时间戳 4.7 使用另…

入职一年半,这个AI员工晋升为了国内首位AI架构师

你敢想&#xff0c;AI 已经不满足只做程序员了&#xff0c;如今又向架构师这一进阶职业发起挑战。 随着 AI 在研发领域的不断进化&#xff0c;能提供给工程师的助力越来越多&#xff0c;从原先单纯的编码加速器不断延展到架构理解、质量分析、安全扫描、测试生成等更多领域。同…

9.1 迭装饰器的定义与使用:给你的 Python 代码加点“魔法”

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

JDBC在java代码中的使用

声明 对于数据库的增删改查&#xff0c;在使用jdbc的过程中分二类&#xff0c;查&#xff08;DQL&#xff09;语句和增&#xff0c;删&#xff0c;改&#xff08;DML语句&#xff09; 他们的整体都分为以下五部分&#xff0c;只是DQL语句多了数据的处理部分。 在使用之前需要…

首屏优化之:import 动态导入

前言 前面我们聊过可以通过不太的 script 属性比如 defer&#xff0c;async 来实现不同的加载效果&#xff0c;从而实现首屏优化&#xff0c;没看的朋友可以看一下&#xff1a;。 今天我们来聊一下动态导入之 import&#xff0c;当然 import 动态导入也不是一把梭的&#xff…

我们需要提高人工智能产品经理的标准

如何停止指责“模型”并开始构建成功的人工智能产品 产品经理负责决定开发什么&#xff0c;并对决策结果负责。这适用于所有类型的产品&#xff0c;包括由人工智能驱动的产品。然而&#xff0c;在过去十年中&#xff0c;产品经理将人工智能模型视为黑匣子是一种常见做法&#x…

如何在 CI/CD 过程中实施高效的自动化测试和部署

文章目录 摘要引言选择适合的 CI/CD 工具常见 CI/CD 工具选择依据 配置自动化构建和测试流程Jenkins示例 制定测试策略单元测试集成测试系统测试 确保部署环境的稳定性蓝绿部署 未来展望总结参考资料 摘要 在持续集成&#xff08;CI&#xff09;和持续交付&#xff08;CD&…

SemanticKernel/C#:实现接口,接入本地嵌入模型

前言 本文通过Codeblaze.SemanticKernel这个项目&#xff0c;学习如何实现ITextEmbeddingGenerationService接口&#xff0c;接入本地嵌入模型。 项目地址&#xff1a;https://github.com/BLaZeKiLL/Codeblaze.SemanticKernel 实践 SemanticKernel初看以为只支持OpenAI的各…

(24)(24.2) Minim OSD快速安装指南(一)

文章目录 前言 1 概述 2 基本接线图 3 关键冷却条件的可选设置 4 固件可用于MinimOSD 5 MWOSD 前言 MinimOSD “屏幕显示”是一个小型电路板&#xff0c;它从你的自动驾驶仪中提取遥测数据&#xff0c;并将其覆盖在你的第一人称视图监视器上(First Person View)。Minim …

发布包到npm

目录 注册npm账号 创建包 登录npm 上架包 更新包 删除包 注册npm账号 首先注册npm账号&#xff1a;npm | Sign Up (npmjs.com) 创建包 可以在桌面上新建一个文件夹&#xff1a;文件夹名随便起&#xff0c;但是别跟npm已经上架的包名重复了 可以通过下面的指令查看&…

【小记】这也算是经验分享了吧~

最近在进行跳槽的一些准备&#xff0c;从简历制作、投递简历、准备面试、视频面试、线下面试、接受录取、辞职准备&#xff0c;每一个过程都超级紧张刺激 大学的时候就有一些制作PPT的经验&#xff0c;靠这个收入了一点点&#xff0c;进而对于office这一系列的操作还是比较熟悉…

韶音Open Fir Air好用吗?南卡、韶音、漫步者三款开放式耳机无广避坑测评!

近期&#xff0c;我注意到后台有许多小伙伴向我咨询如何挑选合适的开放式耳机。市场上开放式耳机品牌琳琅满目&#xff0c;它们在音质表现、佩戴舒适度以及综合性能上均展现出各自的独特魅力与差异。对于追求耳朵极致舒适体验的朋友而言&#xff0c;选择一款合适的开放式耳机显…

从零开始搭建监控系统 (三) 指标采集

从零开始搭建监控系统 (三) 指标采集 背景 Node Exporter就可以用来采集机器的各项指标&#xff0c;从而监控机器的状态。 如果机器上运行了一些小脚本&#xff0c;想要对其进行监控但又不想用上一些在代码里做信息采集的SDK那么重&#xff0c;比如只是单纯想要监控该脚本是…

【C语言】红黑树

红黑树 1.二叉查找树 首先要了解的是二叉查找树&#xff0c;也称为二叉排序树&#xff0c;优点是在节点均匀分布的情况下&#xff0c;查找效率更高&#xff0c;缺点是&#xff0c;如果节点分布在一侧&#xff0c;查找时间就会约等于数组从头到尾的去查找。 二叉查找树的子树…

24/8/8算法笔记 决策回归树

from sklearn.tree import DecisionTreeRegressor from sklearn import tree import numpy as np import matplotlib.pyplot as plt 创建数据 X_train np.linspace(0,2*np.pi,40).reshape(-1,1)#训练数据就是符合要求的二维数据 #二维&#xff1a;[[样本一].[样本二]&#x…

服务器数据恢复—Raid5阵列热备盘上线过程中断导致阵列崩溃的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 两组分别由4块SAS硬盘组建的raid5磁盘阵列&#xff0c;ext3文件系统lvm结构。 磁盘阵列中一块硬盘离线&#xff0c;热备盘自动上线替换离线硬盘并开始同步数据。在热备盘同步数据的过程中该组raid中另外一块硬盘出现故障掉线&#xff…

Docker最佳实践(六):安装Nacos

大家好&#xff0c;欢迎各位工友。 本篇呢我们就来演示一下如何在Docker中部署nacos容器&#xff0c;市面上的教程多多少少都有点小坑&#xff0c;博主磕磕绊绊测试了好几次&#xff0c;才算解决此问题。 1. 拉取Nacos镜像 首先&#xff0c;拉取对应版本的Nacos镜像文件。可以…

【ACM出版,EI稳定检索】第四届信号处理与通信技术国际学术会议(SPCT 2024)

第四届信号处理与通信技术国际学术会议&#xff08;SPCT 2024&#xff09; 2024 4th International Conference on Signal Processing and Communication Technology 重要信息 大会官网&#xff1a;www.icspct.com 大会时间&#xff1a;2024年12月27-29日 大会地点&#xff1a…

Nodejs实现图片加水印 【使用jimp】

Nodejs实现图片加水印 【使用jimp】 先看效果 我们将使用jimp实现图片加上水印&#xff0c;可以结合路由进行用户上传后处理该图片生成带水印的图片返回个用户 const path require("path"); const jimp require("jimp");/*** 给一张图片加水印* para…