通过栈/队列/优先级队列/了解容器适配器,仿函数和反向迭代器

news2024/10/6 6:42:21

文章目录

    • 一.stack
    • 二.queue
    • 三.deque(双端队列)
    • 四.优先级队列
      • 优先级队列中的仿函数
      • 手搓优先级队列
    • 五.反向迭代器
      • 手搓反向迭代器

vector和list我们称为容器,而stack和queue却被称为容器适配器。

在这里插入图片描述

这和它们第二个模板参数有关系,可以看到stack和queue的第二个模板参数的缺省值都是deque,即双端队列容器。也就是说栈和队列都是以容器为主材料构建的,而且因为第二个参数是一个缺省值,我们不但可以使用deque构建,同样可以使用vectorlist来构建。

用已经存在的容器来封装转换成不存在的容器,这种方式就被称之为适配器模式。

有了deque提供的接口,再要实现栈和队列就会变得很简单。

一.stack

栈的特点就是后进先出,,插入和删除都是在尾部进行,栈不提供迭代器(因为栈只能访问栈顶的元素)。

#include<deque>
namespace wbm
{
	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;
	};

}

栈不但可以通过deque来封装,还可以通过vectorlist来封装,只要支持尾插尾删即可

二.queue

队列的特点是先进先出,队尾入,队头出,可以访问队头和队尾的数据,也不提供迭代器

#include<deque>

namespace wbm
{
	template<class T,class Container=deque<T>>
	
	class queue
	{
	public:
		bool empty()const
		{
			return _con.empty();
		}

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

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

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

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

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

	private:
		Container _con;
	};
}

只要是支持尾插头删的容器都可以做queue的适配器,但是不建议使用数组,因为数组的头删效率太低

三.deque(双端队列)

因为vector和list都各有优缺点,因此大佬们就想是否可以创造一个容器兼具vector和list的优点又避免缺点,于是便有了双端队列deque,虽然deque的名字带着队列,但是它和队列毫无关系。

在这里插入图片描述

观察它提供的接口发现:它既支持随机访问,又支持头插头删和尾插尾删,看起来似乎确实是一个完美的容器。但如果真的那么完美,vector和list岂不是早就被淘汰了。

其实deque的底层是一个指针数组+多个buffer数组(buffer数组的大小是固定的)的组成形式;这个指针数组是一个中控数组,其中存放的元素是属于这个deque的buffer数组的地址。

第一次插入数据开辟的buffer数组的地址存放在中控数组的中间元素,如果buffer数组满了要继续尾插,则继续开辟新的buffer数组,此时第二个buffer数组的地址,存放在中控数组中间元素的下一个。如果你要头插,则开辟一个新的buffer数组,并将这个buffer数组的地址存放在中间元素的前一个。

在这里插入图片描述

deque的优点:

1.扩容代价小于vector:它只有在中控满了以后才需要扩容,并且不需要拷贝原生数据,只要将中控数组与buffer之间的映射关系拷贝下来即可。

2.因为是一段一段的空间,所以CPU高速缓存命中率高于list。

3.头尾插入删除效率都较高,并且支持随机访问

但是deque也有自己的缺点:

1.随机访问效率不如vector,它的随机访问要通过计算得到,假设每个buffer数组的大小为size,你要访问第10个元素,首先要10/size来确定这个元素在哪一个buffer数组中,再用10%size来确定这个元素在这个buffer数组中的哪个位置,所以deque也不适合排序,因为排序需要大量的随机访问。

2.中间插入删除不如list,它在中间插入删删同样需要挪动一定量的数据

3.迭代器实现复杂:它的迭代器中有四个指针,当cur==last时,说明本段空间被使用完毕++node继续去访问下一段空间,并更新firstlast

在这里插入图片描述


四.优先级队列

在这里插入图片描述

优先级队列的特点就是优先级高的先出,它也是一个容器适配器,不提供迭代器,底层是一个堆并且默认是大堆。

priority_queue<int> //小堆
priority_queue<int,vector<int>,greater<int>> //大堆

优先级队列中的仿函数

仿函数是一个函数对象,它是一个类函数的对象,要达到类函数,所以这个类中必须要重载()。优先级队列中默认是大堆,如果我们要改成小堆,除了要显示传递第三个参数以外还要更改比较大小的算法。在C语言中,为了能让qsort排序任意类型,库中使用了函数指针的办法,让使用者显示的去写一个比较函数,并将该函数的地址作为参数传递过去。仿函数的一个应用场景就类似于函数指针。

namespace wbm
{
	template<class T>
	struct less //可以使用struct,也可以使用class,它们都是类,只是默认的访问限定不同
	{
		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()
{
	wbm::less<int> func;
	//两者等价:func(1, 2)==func.operator()(1, 2);
	return 0;
}

手搓优先级队列

#include<vector>
#include<iostream>
namespace wbm
{

	template<class T>
	struct less //可以使用struct,也可以使用class,它们都是类,只是默认的访问限定不同
	{
		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<T>>
	class priority_queue
	{
	private:
		void Adjustdown(size_t parent)
		{
			Compare com;//构造一个仿函数对象
			size_t child = parent * 2 + 1;//先默认是左孩子大,如果右孩子更大,把孩子节点更新成右孩子

			while (child < _con.size())
			{
				if (child+1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					++child;//默认是less,也就是左孩子比右孩子小
				}

				//将孩子节点和父节点做比较
				if (com(_con[parent], _con[child])) 
				{
					std::swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
				
			}
		}

		void Adjustup(size_t child)
		{
			Compare com;
			size_t 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:
		priority_queue()
		{
			//要有一个默认构造,不写就会报错
		}

		//迭代器区间构造,直接构造出一个堆,使用向下调整更佳
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			:_con(first, last)  //初始化列表,vector支持迭代器区间构造
		{
			//初始化建堆,使用向下调整,从第一个非叶子节点开始
			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);
		}

		void pop()
		{
			//将首元素和末尾元素换位置删除后调整

			std::swap(_con.front(), _con.back());
			_con.pop_back();
			Adjustdown(0);
		}

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

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

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

	private:
		Container _con;
	};
}

如果优先级队列中存放的是某个类的地址,又需要我们比较地址中值的优先级,那就需要使用仿函数来进行特殊处理。否则只会按照地址来比较。

五.反向迭代器

反向迭代器采用的是适配器模式,是通过正向迭代器的再封装实现的,你给它某个容器的正向迭代器,它就产生这个容器的反向迭代器,它与正向迭代器的位置是对称的并且正好相反。所以要控制反向迭代器,只需要使用运算符重载,篡改方向迭代器中++--的规则就可以。

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

在这里插入图片描述

rbegin和end一样,指向的是最后一个元素的下一个位置,要想访问到3,要先--再访问,这个问题可以通过重载*来解决

template<class Iterator,class Ref,class Ptr>
Ref operator*()
{
    Iterator tmp=it;
    return *(--tmp);
}

手搓反向迭代器

namespace wbm
{
    //反向迭代器,使用正向迭代器封装
    template<class Iterator,class Ref,class Ptr>  //迭代器,T&/const T&,T*/const T*

    class ReverseIterator
    {
        typedef ReverseIterator<Iterator, Ref, Ptr> Self;
    public:
        ReverseIterator(Iterator it)
            :_it(it)
        {}

        Ref operator*()
        {
            Iterator tmp;
            return *(--tmp);
        }

        Ptr operator->()
        {
            return &(operator*())
        }

        Self& operator++()
        {
            --_it;
            return *this;
        }

        Self& operator--()
        {
            ++_it;
            return *this;
        }

        bool operator!=(const Ref& rit)const//两个迭代器之间的比较
        {
            return _it != rit;
        }

    private:
        Iterator _it;//这里给的参数是一个正向迭代器
    };
}

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

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

相关文章

录屏界鼻祖Camtasia 2023中文版功能介绍/下载安装激活教程

随着网络科技的迅速发展&#xff0c;所以对于电脑的使用率也就越来越高了&#xff01;然而&#xff0c;也可能跟这有关系&#xff0c;目前各种类型的软件层出不穷&#xff0c;当然也就包括了电脑录屏软件。这给我们造成了一些困难&#xff0c;究竟哪一款适合自己呢&#xff1f;…

DMA的补充笔记

DMA有两个总线&#xff1a; 1、DMA存储器总线&#xff1a;DMA通过该总线来执行存储器数据的传入和传出。 2、DMA外设总线&#xff1a;DMA通过该总线访问AHB外设&#xff08;AHB主要是针对高效率、高频宽以及快速系统模块所设计的&#xff0c;主要有Flash 存储器、复位和时钟控…

栈和队列OJ题思路分享之栈和队列互换(C语言实现)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:刷题分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你刷更多C语言和数据结构的题!   &#x1f51d;&#x1f51d; 栈和队列刷题分享二 1. 前言⚡…

基于人类反馈的强化学习(RLHF) 理论

gpt 进程 GPT-1 用的是无监督预训练 有监督微调&#xff0c;只有简单的单向语言模型任务&#xff1b;GPT-2用的是纯无监督预训练&#xff0c;使用更多的数据&#xff0c;更大的模型&#xff0c;又新增了几个辅助的训练任务&#xff1b;GPT-3 沿用了 GPT-2 的纯无监督预训练&a…

postgresql standby启动流程分析

专栏内容&#xff1a;postgresql内核源码分析个人主页&#xff1a;我的主页座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物&#xff0e; 目录 前言 概述 原理机制 关键流程 备节点启动 walreceiver启动的时机 结尾 前…

网络协议与攻击模拟-07-TCP连接-三次握手

传输层协议 1.TCP 2.UDP TCP 协议 回顾内容 1&#xff0e;传输层功能&#xff1a;定义应用层协议数据报文的端口号&#xff0c;流量控制 2&#xff0e;对原始数据进行分段处理 传输层所提供服务 1&#xff0e;传输连接服务 2&#xff0e;数据传输服务&#xff1a;流量控制…

基于条纹投影的物体三维形貌测量理论与仿真实验-含Matlab代码

▒▒本文目录▒▒ ✳️ 一、引言✳️ 二、条纹投影原理✳️ 2.1 相移轮廓术✳️ 2.2 傅里叶变换轮廓术 ✳️ 三、仿真分析✳️ 3.1 傅里叶变换轮廓术仿真实验✳️ 3.1.1 Matlab代码获取 ✳️ 3.2 相移轮廓术仿真实验✳️ 3.2.1 Matlab代码获取 ✳️ 参考文献 ✳️ 一、引言 基…

玩转axios, 我行你行吗

一.axios是什么&#xff1f; Axios是一个基于Promise网络请求库,作用于node.js和浏览器中, 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。 在服务端它使用原生node.js http模块&#xff0c;而在客户端(浏览端)则使用XMLHttpRequests。 二.特性 1.从浏览器创…

SpringCloud:微服务保护之流量控制

雪崩问题虽然有四种方案&#xff0c;但是限流是避免服务因突发的流量而发生故障&#xff0c;是对微服务雪崩问题的预防。我们先介绍这种模式。 1.簇点链路 当请求进入微服务时&#xff0c;首先会访问DispatcherServlet&#xff0c;然后进入Controller、Service、Mapper&#…

c++ static修饰类的成员

静态成员就是成员变量和成员函数前面加上static,称为静态成员 1.static修饰成员变量 (1)所有对象共享static成员变量&#xff0c;一旦一个对象数据对其进行改动&#xff0c;所有对象调用其数据也会改变&#xff0c;一改全改。 (2)编译阶段分配内存&#xff0c;static成员变量…

定积分比较大小的常用手段。

1.常用手段 ①区间对称&#xff0c;利用被积函数奇偶性 ②放缩(利用常用不等式,结论等) ③将 1 转换成定积分 ④直接算 ⑤“拆区间&#xff0c;变量代换改区间再合并” 2.常用不等式 1.基本不等式 sinx < x < tanx (0 , Π/2) (几何或者夹逼证明) ex > 1 x (-∞&a…

【数据结构】set 与 map

目录 一、关联式容器 二、set 1、set的介绍 2、set的模板参数列表 3、set的构造 4、set的迭代器 5、set的容量 6、set修改操作 7、set 与 multiset 三、map 1、map的介绍 2、map的模板参数列表 3、map的构造 4、map的迭代器 5、map的容量与元素访问 6、map中元素…

代码随想录算法训练营第五十三天| 1143.最长公共子序列 、1035.不相交的线、53. 最大子序和 动态规划

文章目录 1143.最长公共子序列:star:1035.不相交的线53. 最大子序和 动态规划 1143.最长公共子序列⭐️ 题目链接&#xff1a;代码随想录 解题思路&#xff1a; 1.dp数组&#xff1a;长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp(i)[j]…

记录一次从exe到python源码

找到要解析源码的exe&#xff0c;用7zip解压出来&#xff0c;可以看到打包前的目录&#xff08;这里并不是所有的exe都可以&#xff09;。 如下图所示&#xff0c;由于这个是一个前后端打包的exe&#xff0c;这里的server是后端的服务&#xff0c;resources是前端&#xff0c;这…

【现代机器人学】学习笔记十二:轮式移动机器人

这一章讲移动机器人相关的内容。许久没有更新了&#xff0c;有人私聊我是不是烂尾了。写的这几篇屁文我本来是拿来当自己的笔记本的&#xff0c;不以为有人会认真看。没想到居然还有人追&#xff0c;真是受宠若惊。在这里深表感谢&#xff01;一直没更新其实是因为年后的工作实…

优惠卷秒杀功能、全局唯一ID、乐观锁解决超卖问题、悲观锁实现一人一单、集群下锁失效问题

文章目录 1 全局唯一ID的需求分析2 Redis实现全局唯一Id3 添加优惠卷4 实现秒杀下单5 库存超卖问题分析6 乐观锁解决超卖问题6 悲观锁实现一人一单7 集群环境下的并发问题 1 全局唯一ID的需求分析 每个店铺都可以发布优惠券&#xff1a; 当用户抢购时&#xff0c;就会生成订单…

ASP.NET Core MVC 从入门到精通之序列化

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…

Springboot +Flowable,任务认领和回退(三)

一.简介 有的时候&#xff0c;一个任务节点会存在多个候选人&#xff0c;例如&#xff1a;张三提交一个任务&#xff0c;这个任务即可以由李四处理&#xff0c;又可以由王五处理&#xff0c;那么针对这种多个任务候选人的情况&#xff0c;该如何处理&#xff1f; 二.绘制流程…

并发编程05:Java内存模型之JMM

文章目录 5.1 先从大场面试开始5.2 计算机硬件存储体系5.3 Java内存模型Java Memory Model5.4 JMM规范下三大特性5.5 JMM规范下多线程对变量的读写过程5.6 JMM规范下多线程先行发生原则之happens-before5.6.1 x,y案例说明5.6.2 先行并发原则说明5.6.3 happens-before总原则5.6.…

【面试题】面试官:说说你对MySQL的了解

文章目录 基础篇非关系型数据库和关系型数据库的区别&#xff1f;MySQL 数据库两种存储引擎的区别? 索引篇为什么索引能提高查询速度?聚集索引和非聚集索引的区别&#xff1f;非聚集索引一定回表查询吗?索引这么多优点&#xff0c;为什么不对表中的每一个列创建一个索引呢&a…