【C++】通过栈/队列/优先级队列/反向迭代器了解适配器及仿函数

2023-11-19


需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。


 目录

一、stack

实现一个stack

二、queue

实现一个queue

三、deque(双端对列)了解

1、deque的概念

2、为什么采用deque作为stack和queue的底层容器?

3、deque的缺点

3.1随机访问速度不如vector

3.2中间插入、删除速度不如list

3.3deque不适合遍历及排序

四、优先级队列

1、优先级队列的介绍

2、优先级队列中的仿函数(函数对象)

3、模拟实现优先级队列

五、反向迭代器

1、反向迭代器的底层

2、模拟实现反向迭代器 


vector、list被称为容器,而stack和queue却被称为容器适配器

        stack和queue的类模板中第二个模板参数的意义是底层选用哪种容器来实现容器适配器,官方文档使用deque作为默认容器。当然,生成模板类的时候可以生成数组栈、链式栈或链式队列等,可以自由切换stack和queue的底层结构。这就是stack和queue被称为容器适配器的原因。这种设计模式被称为适配器模式。

        适配器模式:用已有的东西封装转换出想要的东西。

         这是博主另一篇使用C语言实现栈和队列的文章:【数据结构】栈和队列的实现及应用。

一、stack

        栈的特点是先进后出,stack不提供迭代器。

实现一个stack

#include <list>
#include <vector>
#include <deque>
namespace jly
{
	template <class T, class Container=deque<T>>
	class stack
	{
	public:
		bool empty()const
		{
			return _con.empty();
		}
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		size_t size()const
		{
			return _con.size();
		}
		const T& top()const
		{
			return _con.back();
		}
	private:
		Container _con;
	};
}

stack的底层可以是vector或list等支持尾插尾删的线性容器。

二、queue

        队列的特点是先进先出,queue不提供迭代器。

实现一个queue

#include <list>
#include <vector>
#include <deque>
namespace jly
{
	template <class T, class Container = deque<T>>
	class queue
	{
	public:
		bool empty()const
		{
			return _con.empty();
		}
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();
		}
		size_t size()const
		{
			return _con.size();
		}
		const T& front()const
		{
			return _con.front();
		}
		const T& back()const
		{
			return _con.back();
		}
	private:
		Container _con;
	};
}

queue的底层可以是list等支持尾插头删的线性容器,但并不支持vector,因为数组头插头删效率不行。

三、deque(双端对列)了解

1、deque的概念

        deque是具有动态大小的顺序容器,可以在两端扩展或收缩。

2、为什么采用deque作为stack和queue的底层容器?

        deque强就强在头尾插入删除数据效率高。

        1、在栈和队列这种只在头尾进行数据操作的结构中,并不需要遍历。

        2、栈的元素增长时,vector的扩容需要挪动数据,而deque扩容时并不需要对原生数据进行挪动,只需拷贝中控数组和段空间的映射关系即可。队列的元素增长时,deque效率高,同时三级缓存命中率及空间利用率比list高。

3、deque的缺点

        deque也有它的缺点。

3.1随机访问速度不如vector

        由于deque的中控数组中指向的一段段地址空间之间并不连续,所以随机访问时需要计算目标数据处于哪段buffer中的第几个数据。计算方式与磁盘定位扇区类似(LBA地址转化为CHS地址)。所以deque的随机访问速度并没有vector快。

3.2中间插入、删除速度不如list

        从deque的底层结构图中可以看出,中间插入、删除数据仍会产生数据的挪动。deque中间插入、删除数据的速度不如list。

3.3deque不适合遍历及排序

        deque迭代器原理:当cur等于last,说明本段空间已被使用完毕,通过node++找到中控数组中下一段内存空间的地址。在遍历时,deque的迭代器需要频繁判断cur的位置,造成效率低下。

四、优先级队列

1、优先级队列的介绍

        优先级队列的特点是优先级高的先出。

        优先级队列是一种容器适配器不提供迭代器,它的底层是一个堆(默认是大堆),这个堆默认使用vector进行适配。

priority_queue<int> pq;//pq底层为大堆
priority_queue<int,vector<int>,greater<int>> pq;//pq底层为小堆

这是博主使用C语言写的一篇关于堆的文章:【数据结构】动图详解二叉树——堆及堆排序

2、优先级队列中的仿函数(函数对象)

        仿函数,又称函数对象,它是一个类,类中重载了函数调用符号();这个运算符重载的返回值根据需要去给。

namespace jly
{
	template <class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template <class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
}
int main()
{
	jly::less<int> func;
	func(1,2);
	func.operator()(1,2);//等价
	return 0;
}

看到func(1,2);会让人感觉这是函数调用,但其实这里的func是一个对象,通过对象,调用运算符重载operator()。

3、模拟实现优先级队列

#pragma once
#include <vector>
namespace jly
{
	template <class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};

	template <class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};



	template <class T, class Container = std::vector<T>,class Compare=less<int>>
	class priority_queue
	{
	private:
		void AdjustUp(size_t child)
		{
			Compare com;//构造一个仿函数对象
			size_t parent = (child - 1) / 2;//当child=0时,parent会很大
			while (parent<_con.size()&&com(_con[parent] ,_con[child]))
			{
				std::swap(_con[parent], _con[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
		}
		void AdjustDown(size_t parent)
		{
			Compare com;//构造一个仿函数对象
			size_t minChild = 2 * parent + 1;
			while (minChild<_con.size())
			{
				if (minChild + 1 < _con.size() && com(_con[minChild] , _con[minChild + 1]))
				{
					++minChild;
				}
				if(com(_con[parent], _con[minChild]))
				{
					std::swap(_con[minChild], _con[parent]);
					parent = minChild;
				}
				else
					break;
			}
		}
	public:
		//构造函数
		priority_queue()
		{
		
		}
		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			:_con(first,last)
		{
			for (int i = (_con.size - 1 - 1) / 2; i >=0 ; --i)
			{
				AdjustDown(i);
			}
		}
		void push(const T& x)
		{
			_con.push_back(x);
			AdjustUp(_con.size()-1);//从size-1开始调整
		}
		void pop()
		{
			std::swap(_con.front(), _con.back());//交换首尾数据
			_con.pop_back();//尾删
			AdjustDown(0);
		}
		const T& top()const
		{
			return _con.front();
		}
		bool empty()const
		{
			return _con.empty();
		}
		size_t size()const
		{
			return _con.size();
		}
	private:
		Container _con;
	};
}

        仿函数和C语言的函数指针的作用有点类似,函数指针是传入目标函数的地址,通过函数回调来执行目标函数的功能。而仿函数是一个类,对于模板函数,根据传入函数的类对象调用类中的operator()来控制不同的结果;对于模板类,通过在类中构造仿函数对象,在需要功能“分叉”的地方调用仿函数类中的operator()来达到不同的效果。(白话:泛型中的仿函数影响逻辑,通过仿函数来控制一个“水龙头”出冷水还是出热水······)

        还有一种情况:优先级队列存的是某个类的地址,但是需要比较指针指向值的优先级。那这个时候就不能用<functional>中的less和greater控制建堆,需要自己写仿函数。(仿函数也可以加上模板特化)

五、反向迭代器

1、反向迭代器的底层

        反向迭代器的本质就是对正向迭代器的封装,它同样是一个适配器。

        STL源码中的反向迭代器的实现:反向迭代器用正向迭代器进行构造。

reverse_iterator rbegin(){return reverse_iterator(end());}
reverse_iterator rend(){return reverse_iterator(begin());}

画个图,其实正向迭代器和反向迭代器的位置是对称的。

        通过图示,rbegin()迭代器是最后一个数据的下一个位置,但是通过rbegin()访问的数据应该是4,这是通过运算符重载operator*()来解决。

template <class Iterator, class Ref, class Ptr>
Ref operator*()
{
    Iterator tmp = _it;
    return *--(tmp);
}
typedef ReserveIterator<iterator, T&, T*> reverse_iterator;
typedef ReserveIterator<const_iterator, const T&, const T*> const_reverse_iterator;

2、模拟实现反向迭代器 

namespace jly
{
	template <class Iterator, class Ref, class Ptr>
	class ReserveIterator
	{
	public:
		ReserveIterator(Iterator it)//使用正向迭代器构造反向迭代器
			:_it(it)
		{

		}
		Ref operator*()
		{
			Iterator tmp = _it;
			return *--(tmp);
		}
		Ptr operator->()//operator->()返回数据的地址
		{
			return &(operator*());
		}
		ReserveIterator<Iterator, Ref, Ptr>& operator++()//返回值不要写成Iterator,因为反向迭代器++返回的是反向迭代器,不是传入的迭代器类型
		{
			--_it;
			return *this;
		}
		ReserveIterator<Iterator, Ref, Ptr>& operator--()
		{
			++_it;
			return *this;
		}
		bool operator!=(const ReserveIterator<Iterator, Ref, Ptr>& rit)const
		{
			return _it != rit._it;
		}
	private:
		Iterator _it;//底层是传入类型的迭代器
	};
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】通过栈/队列/优先级队列/反向迭代器了解适配器及仿函数 的相关文章

  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • C++ 多行字符串原始文字[重复]

    这个问题在这里已经有答案了 我们可以像这样定义一个多行字符串 const char text1 part 1 part 2 part 3 part 4 const char text2 part 1 part 2 part 3 part 4
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 为什么 isnormal() 说一个值是正常的,而实际上不是?

    include
  • C++ 中的 include 和 using 命名空间

    用于使用cout 我需要指定两者 include
  • 当文件流没有新数据时如何防止fgets阻塞

    我有一个popen 执行的函数tail f sometextfile 只要文件流中有数据显然我就可以通过fgets 现在 如果没有新数据来自尾部 fgets 挂起 我试过ferror and feof 无济于事 我怎样才能确定fgets 当
  • 为什么 std::uint32_t 与 uint32_t 不同?

    我对 C 有点陌生 我有一个编码作业 很多文件已经完成 但我注意到 VS2012 似乎有以下语句的问题 typedef std uint32 t identifier 不过 似乎将其更改为 typedef uint32 t identifi
  • C# 使用“?” if else 语句设置值这叫什么

    嘿 我刚刚看到以下声明 return name null name NA 我只是想知道这在 NET 中叫什么 是吗 代表即然后执行此操作 这是一个俗称的 条件运算符 三元运算符 http en wikipedia org wiki Tern
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐

  • awk读取ini配置文件

    awk读取ini配置文件 一 awk基础 二 读取ini 1 net ini文件 2 打印 三 读取特定Section的Key的值 1 设置特定值 2 查找匹配项 四 总结 一 awk基础 F 指定分割符 print 打印 0 表示整个当前
  • 软件项目管理与开发流程管理 课程

    软件项目管理与开发流程管理 课程背景 以 IT领域典型的软件开发项目管理为主线 结合业界公认最成功的Rational软件开发统一流程架构 Rational Unified Process 和奉为项目管理圣经的美国项目管理协会 PMI 项目管
  • mnist数据集及其读写格式

    mnist数据集及其读写格式 1 mnist 数据集 2 idx 数据格式 参考文献 1 mnist 数据集 mnist数据是手写的数字0 9的数据集 共包含训练集60000个样本和测试集10000个样本 mnist是NIST的子集 同时m
  • Sa-Token获取当前所有可用Token

    记录一下写的小工具 里面的逻辑只能让各位大佬自己看了 各位用的时候自己改改 TokenInfo这个类是自定义的 我这里是获取了一下当前token对应的用户最大的生命周期 各位大佬们自行享用了 获取所有有效token集合 return pub
  • Python -- The eric Python IDE

    Python The eric Python IDE ataru 21 Jul 2008 14 26 最近在看Qt4 結果就看到eric這個為了Python與Ruby開發的IDE工具 它本身是用Python Qt寫出來的 因此這正可以當作希
  • 我的世界1.12 Java崩溃,1.12.2崩溃报告求助

    官方认证版本 mod都是在百科或者CurseForge上下载的 每次崩溃都没得前兆 突然崩溃 之后打开存档就会直接崩溃 crash reports如下 Minecraft Crash Report WARNING coremods are
  • STM32使用HAL库实现按键的单击、双击、长按

    STM32使用HAL库实现按键的单击 双击 长按 目录 STM32使用HAL库实现按键的单击 双击 长按 前言 具体思路 工程配置 代码实现 实验效果 扫描以下二维码 关注公众号雍正不秃头获取更多STM32资源及干货 前言 编程开发环境 S
  • 【完结版】jmeter+ant+python自动化框架,且支持jenkins持续集成

    前言 本文是实现jmeter ant python脚本的自动化测试框架 并且把整套部署在jenkins 通过jenkins的构建来出发脚本的运行 而且还会在jenkins上展示html报告 本文记录搭建框架的整个步骤 以及遇到的问题和记录解
  • css 排除具有某个class的项

    idname li not classA display inline block 增加样式时将li中有class的去掉
  • 使用决策树进行特征选择

    使用决策树进行特征选择 决策树也是常用的特征选取方法 使用决策树集合 如随机森林等 也可以计算每个特征的相对重要性 这些重要性能够辅助进行特征选择 该方法主要使用信息增益率来进行特征选择 from sklearn import datase
  • Keil的软件仿真和硬件仿真

    一 软件仿真 Keil有很强大的软件仿真功能 通过软件仿真可以发现很多将要出现的问题 Keil的仿真可以查看很多硬件相关的寄存器 通过观察这些寄存器值的变化可以知道代码有没有正常运行 这样可以避免频繁下载程序 延长单片机Flash寿命 开始
  • RNN Pytorch实现——up主:刘二大人《PyTorch深度学习实践》

    b站up主 刘二大人 PyTorch深度学习实践 教程 https www bilibili com video BV1Y7411d7Ys p 6 vd source 715b347a0d6cb8aa3822e5a102f366fe 单层
  • 【2023最新版】Win11: WSL(Ubuntu22.04)使用docker远程容器教程(Windows的Docker Desktop下载安装、迁移到非系统盘、配置国内镜像源、设置 WSL2)

    目录 一 准备工作 1 安装WSL 适用于 Linux 的 Windows 子系统 2 docker简介 来源chatGPT 二 Windows安装 Docker Desktop 1 官网链接 2 安装过程 3 迁移到非系统盘 4 配置国内
  • 怎么用vscode进行单步调试

    1 修改launch文件 version 0 2 0 configurations name gdb Launch type cppdbg request launch program workspaceFolder build my cm
  • springcloud学习记录一、了解微服务

    此学习通过查阅相关资料 自己理解的方式进行总结 没有用太多的官方语言 官方语言一直不喜欢 搞得高大上 其实很简单 就是能装那个啥 看的人头疼 尤其是对新手 如果有人发现有问题请指正 谢谢 一 单机结构 对于一个小项目 并且使用人数不多时 开
  • @注解学习总结

    RestController 相当与 Controller ResponseBody组合使用 其作用均是将方法的结果转化为JSON 格式返回给前端 这点可以从 Postman 中体现 RequestMapping value 和 path
  • QT调试详细操作步骤及案例分析

    目录 QT调试详细操作步骤及案例分析 QT调试详细步骤 1 手动调试 1 1 输入备调试的代码 1 2 设置断点 1 3 单步调试简单介绍 1 4 调试案例 1 4 1 纯C 代码的调试 1 4 2 QT程序的调试 2 使用QDebug进行
  • 在centos中配置linux网络ping时碰到destination host unreachable的问题

    最近装了个VM VirtualBox和CentOS玩Linux 在配置完网络后 只能ping 127 0 0 1 无法ping出本机IP和外网 出现 destination host unreachable 的报错 From 192 168
  • 如何写一篇简洁易懂的测试报告?

    一 什么是测试报告 测试报告是指把测试的过程和结果写成文档 对发现的问题和缺陷进行分析 为纠正软件的存在的质量问题提供依据 同时为软件验收和交付打下基础 二 测试报告的内容 测试报告的内容可以总结为以下目录 首页 引言 目的 背景 缩略语
  • 【C++】通过栈/队列/优先级队列/反向迭代器了解适配器及仿函数

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 stack 实现一个stack 二 queue 实现一