【C++修炼之路】stl 中的容器适配器

news2024/11/22 21:55:03

👑作者主页:@安 度 因
🏠学习社区:StackFrame
📖专栏链接:C++修炼之路

文章目录

  • 一、stack
  • 二、queue
  • 三、deque
  • 四、priority_queue
    • 1、仿函数
    • 2、实现

如果无聊的话,就来逛逛 我的博客栈 吧! 🌹

stack, queue, priority_queue 都是容器适配器。所谓容器适配器就是封装了一个容器,数据存在容器里,根据要求改造,控制,适配出需要的接口。适配器的本质是一种复用

一、stack

// Container 为适配器
template<class T, class Container = vector<T>>
    class stack
    {
        public:
        void push(const T& x)
        {
            _con.push_back(x);
        }

        void pop()
        {
            _con.pop_back();
        }

        T& top()
        {
            return _con.back();
        }

        size_t size()
        {
            return _con.size();
        }

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

        private:
        Container _con; // 根据模板实例化为链式 or 数组
    };

二、queue

实现时适配器默认给为 list 最好。因为 stl 官方库中, pop 接口调用的是 pop_front ,而 vector 没有这个接口(效率不高),实现 pop 接口时,与库统一使用头删,默认使用 list 容器。

由上得,这种实例化方式是不支持的:queue<int, vector<int>> ,因为库中使用的头删,vector 并没有对应接口。

template<class T, class Container = list<T>>
    class queue
    {
        public:
        void push(const T& x)
        {
            _con.push_back(x);
        }

        void pop()
        {
            _con.pop_front(); // vector 没有,库中调用的是 pop_front,所以库不支持 vector
            // _con.erase(_con.begin()); // 可以强制适配,不建议,1. vector 头删效率较低;2. 与库中保持一致
        }

        T& front()
        {
            return _con.front();
        }

        T& back()
        {
            return _con.back();
        }

        size_t size()
        {
            return _con.size();
        }

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

        private:
        Container _con;
    };

三、deque

stack, queue 默认的适配器是双端队列 deque

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。

image-20230723142203865

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际 deque 类似于一个动态的二维数组,其底层结构如下图所示:

image-20230723142312326

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

image-20230723142352365

那deque是如何借助其迭代器维护其假想连续的结构呢?

image-20230723142412322

deque 的缺陷

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。

与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。

但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。

为什么选择deque作为stack和queue的底层默认容器:

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

  1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。结合了deque的优点,而完美的避开了其缺陷。

四、priority_queue

优先级队列 priority_queue 使用的默认适配器是 vector ,本质为堆,堆是完全二叉树,为数组形式。其中需要进行大量的 [] ,使用 deque 作为默认适配器效率不高,但是也能用。

优先级队列优先出优先级高的元素,默认大的高,所以默认是大堆,可以通过仿函数控制大小堆。

大堆需要的仿函数 less ,小堆需要的仿函数 greater ,大小相反。

priority_queue<int, vector<int>, less<int>> pq; // priority_queue<int> pq 大
priority_queue<int, vector<int>, greater<int>> pq; // 小

1、仿函数

仿函数看起来像函数名,功能类似函数指针,但是比函数指针的功能更加完善。例如仿函数可以使用类模板,对于各种类型的比较更加便捷。

实际上就是重载了 () ,对于内置类型直接比较,自定义类型比较的前提是重载了类型的比较符号。

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

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

当然也有这种的比较方式:

优先级队列里存的是指针,需要比较指针指向的内容,所以可以不用模板,写特定类型的比较:

class LessDate
{
    public:
    bool operator()(const Date* pd1, const Date* pd2)
    {
        return *pd1 < *pd2;
    }
};

2、实现

其他的实现则和之前的堆相似:

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

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

	template<class T, class Container = vector<T>, class Compare = Less<T>>
	class priority_queue
	{
	private:
		// 提供给成员函数的函数,设为私有
		void AdjustDown(int parent)
		{
			Compare com; // 另一种泛型,存储的是比较的类型
			int child = 2 * parent + 1;

			while (child < _con.size())
			{
				// if (child + 1 < _con.size() && _con[child + 1] > _con[child])
                 //
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) // 2 > 1 就是 1 < 2 
				{
					++child;
				}

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

		void Adjustup(int child)
		{
			Compare com;

			int parent = (child - 1) / 2;

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


	public:
		priority_queue()
		{}
		
        // 迭代器构造
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}

			// 向下调整建堆:O(N)
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
				AdjustDown(i);
			}
		}

		void push(const T& val)
		{
			_con.push_back(val);
			Adjustup(_con.size() - 1);
		}

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

		size_t size()
		{
			return _con.size();
		}

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

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

	private:
		Container _con;
	};

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

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

相关文章

从新手到大师:优雅的Vim熟练之旅(万文详解)

从新手到大师&#xff1a;优雅的Vim熟练之旅 博主简介一、前言1.1、Vim编辑器的重要性和流行性1.2、目标 二、Vim简介2.1、什么是Vim2.2、历史和背景简介2.3、Vim的优势和适用场景 三、安装和设置Vim3.1、下载和安装Vim编辑器3.2、基本配置&#xff1a;.vimrc文件的重要性和常用…

Spinger ESE独立出版|2023年第二届能源与环境工程国际会议(CFEEE 2023)

会议简介 Brief Introduction 2023年第二届能源与环境工程国际会议(CFEEE 2023) 会议时间&#xff1a;2023年9月1日-3日 召开地点&#xff1a;中国三亚 大会官网&#xff1a;CFEEE 2023-2023 International Conference on Frontiers of Energy and Environment Engineering 由I…

leetcode 491. 递增子序列

2023.7.23 本题本质上也是要选取递归树中的满足条件的所有节点&#xff0c;而不是选取叶子节点。 故在将符合条件的path数组放入ans数组后&#xff0c;不要执行return。 还一点就是这个数组不是有序的&#xff0c;并且也不能将它有序化&#xff0c;所以这里的去重操作不能和之前…

MyBatis框架提供的分页助手插件pagehelper

使用MyBatis框架提供的分页助手插件可以很方便地实现分页查询。以下是一个基于MyBatis分页助手插件完成分页查询的示例&#xff1a; 1.首先&#xff0c;确保在项目的依赖中添加了MyBatis分页助手插件的依赖&#xff0c;例如&#xff1a; <dependency><groupId>co…

【C语言】getchar和putchar函数详解:字符输入输出的利器

目录 &#x1f4cc;getchar函数 ▪️ 函数原型&#xff1a; ▪️ 目的&#xff1a; ▪️ 返回值&#xff1a; ▪️ 用法&#xff1a; &#x1f4cc;putchar函数 ▪️ 函数原型&#xff1a; ▪️ 目的&#xff1a; ▪️ 参数&#xff1a; ▪️ 返回值&#xff1a; ▪…

20 QTreeWidget控件

代码&#xff1a; //treeWidget树控件//1&#xff1a;设置头部标签 QStringList()匿名对象创建ui->treeWidget->setHeaderLabels(QStringList()<<"英雄"<<"英雄介绍");//2&#xff1a;设置itemQTreeWidgetItem * liItem new QTreeWidg…

刘铁猛C#教程笔记——操作符

C#语言中的操作符 表中位于同一行的操作符优先级相同&#xff0c;从上到下优先级依次减弱&#xff1b; 操作符的用法举例 成员访问运算符——“.”&#xff1a;用于访问类中的成员或者访问位于某个名空间中的类&#xff0c;如&#xff1a; using System; using System.Collec…

Unity进阶--fsm状态机的使用笔记

文章目录 Unity进阶--fsm状态机的使用笔记第一种用基础的if播放实现动画控制switch--case实现状态机使用状态机 Unity进阶–fsm状态机的使用笔记 第一种用基础的if播放实现动画控制 朴实无华&#xff0c;简单易懂&#xff0c;但是耦合性太差。 switch–case实现状态机 写对应…

【JAVA】云HIS系统功能菜单知识(二)

随着医疗信息化和互联网技术的不断发展&#xff0c;云HIS在大数据管理和应用的优势日益凸显。对于医疗机构而言&#xff0c;云HIS平台可以帮助其实现更高效的医疗服务管理&#xff0c;并提高医疗服务的整体水平和效率。 一、系统管理 1.医院信息 基本信息、法人代表、主要负责…

IntelliJ IDEA2023中利用maven-archetype-quickstart模板创建项目无src文件夹及maven插件下载过慢问题的解决

目录 介绍问题之解决问题2的解决问题1的解决 介绍 昨天下载并安装了IntelliJ IDEA 2023的最新版&#xff08;以下简称为IDEA 2023&#xff09;&#xff0c;学习利用该IDE编写Java项目及将其与maven结合构建项目。我所安装的maven是去年暑假安装的&#xff0c;版本为Apache Mav…

【架构基础】架构概念

软件架构产生的背景 1972年图灵奖获得者、荷兰计算机科学家Edsger Wybe Dijkstra早在20世纪60年代就开始涉及软件架构概念了。 20世纪60年代第一次软件危机引出了结构化编程&#xff0c;创造了模块的概念。 20世纪80年代第二次软件危机引出了面向对象编程&#xff0c;创造了…

Flask的send file和send_from_directory的区别

可以自行查看flask 文档。 send file高效&#xff1b; send from directory安全&#xff0c;且适用于静态资源交互。 都是实现相同的功能的。 send_file send_from_directory

所有docker命令无效,解决办法

目录 ■前言 今天使用docker时&#xff0c;所有命令无效 ■解决办法如下 1.停止docker服务 2.查看状态 3.删除之前的docker相关的文件 4.再次查看状态 5.使用相关命令 &#xff08;好用了&#xff09; 6.重新下载镜像 ■前言 今天使用docker时&#xff0c;所有命令无…

MySQL 8.0 OCP (1Z0-908) 考点精析-备份与恢复考点1:MySQL Enterprise Backup概要

文章目录 MySQL 8.0 OCP (1Z0-908) 考点精析-备份与恢复考点1&#xff1a;MySQL Enterprise Backup概要MySQL Enterprise Backup下载与安装MySQL Enterprise Backup的备份过程MySQL Enterprise Backup的优势mysqlbackup 客户端例题例题1 &#xff1a; MySQL Enterprise Backup概…

idea的插件FastRequest,比postman更好用

1.安装插件Restful Fast Request 在插件plugin中直接搜索Restful Fast Request,然后点击install安装 2.使用插件 插件位置在右面&#xff0c;点开后呈现以下页面 配置项目名和环境 选择配置好的项目名和环境 启动项目后可以看到接口的小火箭&#xff0c;点击小火箭 3.…

【配置环境】Windows下 VS Code 远程连接虚拟机Ubuntu

一&#xff0c;环境 Windows 11 家庭中文版VMware Workstation 16 Pro &#xff08;版本&#xff1a;16.1.2 build-17966106&#xff09;ubuntu-22.04.2-desktop-amd64 二&#xff0c;关键步骤 Windows下安装OpenSSHVS Code安装Remote - SSH插件 三&#xff0c;详细步骤 在Ubun…

sentinel深入讲解流量控制/熔断降级

文章目录 sentinelsentinel介绍重要的核心概念引入依赖限流的规则熔断规则yaml 项目配置使用注解 SentinelResource讲解类的静态方法 sentinel sentinel介绍 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构…

STM32中PWM概述

STM32F103C8T6 PWM资源&#xff1a; 高级定时器(TIM1):7路 通用定时器(TIM2~TIM4):各4路 PWM输出模式&#xff1a; *PWM模式1&#xff1a;在向上计数时&#xff0c;一旦CNT<CCRx时输出为有效电平&#xff0c;否则为无效电平 *PWM模式2&#xff1a;在向上计数时&#xf…

vue实现仿手写稿样式,可导出成png图片

文章目录 环境实现效果代码 环境 安装html2canvas&#xff0c;用于将指定标签下的全部子节点转换为图片 npm install html2canvas实现 <template><div class"handwrite"><div id"left" class"left"><div id"backImg…

【Mycat2】关于序列功能的一个 Bug

创建序列前 Mycat 的 sequences/ 目录情况&#xff1a; 创建一个 MySQL 生成方式的序列。 /* mycat:setSequence{"name":"sharding_db_not_tb111","clazz":"io.mycat.plug.sequence.SequenceMySQLGenerator"} */;因为没有官方文档支…