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

news2024/12/23 14:47:15


目录

一、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 = 0; i < (_con.size-1-1)/2; ++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;//底层是传入类型的迭代器
	};
}

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

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

相关文章

node.js——http模块

文章目录什么是 http 模块创建最基本的 Web 服务器request 请求对象response 响应对象解决中文乱码问题根据不同的 url 响应不同的 html 内容文件上传实战什么是 http 模块 http 模块是 Node.js 官方提供的、用来创建 Web 服务器的模块。 node.js提供了http模块&#xff0c;其…

【VCS Verdi】VCS Verdi 联合仿真总结

1. VCS 介绍VCS是编译型 Verilog 模拟器&#xff0c;它完全支持 OVI 标准的 Verilog HDL 语言、PLI 和 SDF。VCS 具有行业中较高的模拟性能&#xff0c;其出色的内存管理能力足以支持千万门级的 ASIC 设计&#xff0c;而其模拟精度也完全满足深亚微米 ASIC Sign-Off 的要求。VC…

C++模拟实现优先级队列(priority_queue)

目录 一、 仿函数 1.1仿函数的概念使用 1.2模拟实现仿函数 二、优先级队列&#xff08;priority_queue) 2.1 优先级队列概念 2.2 优先级队列使用 2.3 模拟实现优先级队列 2.3.1 优先级队列类框架 2.3.2 模板参数 2.3.3 构造函数 2.3.4 仿函数 2.3.5 adjust_up (堆向…

linux系统中QT里面信号与槽的实现方法

大家好&#xff0c;今天主要来聊一聊&#xff0c;QT中信号与槽的使用方法。 目录 第一&#xff1a;QT中信号与槽简介 第二&#xff1a;如何在项目里创建信号 第三&#xff1a;如何在项目中创建槽 第四&#xff1a;项目中连接信号与槽 第一&#xff1a;QT中信号与槽简介 在学…

遥感图像处理:最小噪声分离变换(Minimum Noise Fraction Rotation,MNF Rotation)

遥感图像处理&#xff1a;最小噪声分离变换&#xff08;Minimum Noise Fraction Rotation&#xff0c;MNF Rotation1.PCA变换2.MNF3.PCA和MNF1.PCA变换 在统计学中&#xff0c;主成分分析PCA是一种简化数据集的技术。它是一个线性变换。这个变换把数据变换到一个新的坐标系统中…

Django使用Celery异步发送短信(Django4.1.3+Celery5.2.7+ubuntu)

首先要下载Celery&#xff0c;直接pip就好 我的redis配置 CACHES {"default": {"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://192.168.2.128:6379/0","OPTIONS": {"CLIENT_CLASS"…

自动化测试Seleniums~2

webdriver API 1.如何打开网页以及如何关闭一个浏览器。 package test_20230107;import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver;import static java.lang.Thread.sleep;public class Test {public static void main(String[] args)…

JavaEE多线程-线程的状态和安全问题

目录一、线程中的基本状态二、线程安全问题三、线程安全的标准类四、synchronized 关键字-监视器锁monitor locksynchronized 的特性五、volatile 关键字一、线程中的基本状态 NEW: 安排了工作, 还未开始行动, 就是创建了Thread对象, 但还没有执行start方法(内核里面还没有创建…

SpringSecurity(十一)【跨域】

十一、跨域 简介 跨域问题实际应用开发中的一个非常常见的需求&#xff0c;在 Spring 框架中对于跨域问题的处理方案有好几种&#xff0c;引入了 Spring Security 之后&#xff0c;跨域问题的处理方案又增加了 什么是 CORS CORS&#xff08;Cross-Origin Resource-Sharing&…

联合证券|重磅数据出炉,道指飙涨700点!美股新年首周“开门红”!

美股涨嗨了&#xff01; 当地时间1月6日&#xff0c;最新发布的美国12月非农工作陈述显现&#xff0c;美国工作市场终于呈现降温迹象&#xff0c;过去一年的激进加息成效初显。受此提振&#xff0c;美股三大股指高开高走&#xff0c;彻底改变前几日的跌势。从周k线看&#xff…

PCB结构和谐振(三)

PCB结构和谐振&#xff08;一&#xff09;PCB结构和谐振&#xff08;二&#xff09;仿真研究在本节中&#xff0c;我们首先对玻璃束的随机分布进行了简单的模拟研究。然后我们利用这些实验结论来简化常用的玻璃布3D结构。最后&#xff0c;这种简化的结构用于研究复杂层压板和两…

Java设计模式中原型模式是啥/浅克隆与深克隆又是什么/clone方法怎么回事

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 4.5 原型模式 4.5.1 概述 用已创建实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象一样的新对象 4.5.2 结构 抽象原型类&#xff1a;规定具体…

Docker系列 深度使用nextcloud(九) 硬盘挂载

转自我的个人博客https://blognas.hwb0307.com&#xff0c;该文的内容更新仅在个人博客可见。欢迎关注&#xff01; 前言 基于《Docker系列 搭建个人云盘服务nextcloud》&#xff0c;相信无论是在有/无443端口的Linux机子里均可成功安装Nextcloud。值得一提的是&#xff0c;Ne…

02、做点准备工作 osg\openscenegraph源代码下载 C++三维视频融合实战系列(时空克隆)

首先&#xff0c;要有一点C编程基础&#xff0c;熟悉VS2013开发环境。 在开始实践之前&#xff0c;先要搭建号VS2013开发环境。 然后&#xff0c;建议电脑安装windows 10 64位操作系统。 接下来需要在以下地址下载开源代码&#xff1a; 1、osg下载 打开openscenegraph主页…

CSS权威指南(七)视觉格式化

文章目录1.盒模型2.元素的显示方式3.行内元素1.盒模型 不管是什么元素,CSS都假定每个元素会生成一个或多个矩形框,我们称之为元素。各元素框的中心是内容区域&#xff0c;四周有可选的内边距、边框、轮廓和外边距。默认情况下&#xff0c;内容区的背景出现在内边距范围内。外边…

5分钟搞懂用户态,内核态

5分钟搞懂用户态,内核态 1. 什么是用户态,内核态 用户态就是提供应用程序运行的空间&#xff0c;为了使应用程序访问到内核管理的资源例如CPU&#xff0c;内存&#xff0c;I/O。内核必须提供一组通用的访问接口&#xff0c;这些接口就叫系统调用。 用户态&#xff0c;内核态…

进制详解:二进制、八进制和十六进制

进制详解&#xff1a;二进制、八进制和十六进制 背景&#xff08;Contexts&#xff09; 我们平时使用的数字都是由 0~9 共十个数字组成的&#xff0c;例如 1、9、10、297、952 等&#xff0c;一个数字最多能表示九&#xff0c;如果要表示十、十一、二十九、一百等&#xff0c;…

机器学习笔记之深度信念网络(一)背景介绍与模型表示

机器学习笔记之深度信念网络——背景介绍与模型表示引言深度信念网络场景构建深度信念网络的联合概率分布引言 从本节开始&#xff0c;将介绍深度信念网络。 深度信念网络 深度信念网络(Deep Belief Network,DBN)是杰弗里辛顿(Geoffrey Hinton)于2006年提出的模型&#xff0…

Day853.WorkerThread模式 -Java 性能调优实战

WorkerThread模式 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于WorkerThread模式的内容。 Thread-Per-Message 模式&#xff0c;对应到现实世界&#xff0c;其实就是委托代办。这种分工模式如果用 Java Thread 实现&#xff0c;频繁地创建、销毁线程非常影响性能…

场景编程集锦 - 吉米的总统梦想

1. 场景描述 吉米是太平洋岛国一个贫苦家庭的孩子&#xff0c;他的梦想就是当总统&#xff0c;引领国家走向富强之路。 开学的第一堂课上&#xff0c;老师用白色的粉笔在黑板上写下了“我的梦想”&#xff0c;同学们都陷入了思考。大卫的梦想是当一名科学家&#xff0c;用奇思妙…