C++笔记---stack和queue

news2024/12/28 13:59:25

1. stack的介绍及重要接口

stack---栈,是一种“先进后出,后进先出”的数据结构。

此处的stack是STL库中定义的一个类模板,用于实例化出存储各种类型数据的栈。

bool empty() const;判断栈是否为空(空true/非空false)
size_t size() const;返回栈中的数据个数
T& top();
const T& top() const;
返回栈顶元素(目前最早进最后出的元素)的引用
void push(const T& val);入栈(到栈顶)
void pop();出栈(删除栈顶元素)
void swap(stack& x);交换两个栈的内容

2. queue的介绍及重要接口

queue---队列,是一种“先进先出,后进后出”的数据结构。

此处的queue是STL库中定义的一个类模板,用于实例化出存储各种类型数据的队列。

bool empty() const;判断队列是否为空(空true/非空false)
size_t size() const;返回队列中的数据个数
T& front();
const T& front() const;
返回队头元素(目前最早进最先出的元素)
T& back();
const T& back() const;
返回队尾元素(目前最后进最后出的元素)
void push(const T& val);入队列(到队尾)
void pop();出队列(删除队头元素)
void swap(queue& x);交换两个队列的内容

3. stack和queue的模拟实现

3.1 适配器模式

在STL标准库中,stack和queue并没有被划分为容器,而是被定义为容器适配器。

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口

如果有数据结构的知识基础就能知道,stack和queue的底层就是顺序表,是数据访问受到限制的顺序表。

于是,我们可以对顺序表及其接口进行包装,进而实现stack和queue。

3.2 stack的模拟实现

namespace lbz
{
	template<class T, class container = deque<T>>
	class stack
	{
	public:
		stack(const container& ctnr = container())
			:_con(ctnr)
		{}
		bool empty() const
		{
			return _con.empty();
		}
		size_t size() const
		{
			return _con.size();
		}
		T& top()
		{
			return _con.back();
		}
		const T& top() const
		{
			return _con.back();
		}
		void push(const T& val)
		{
			_con.push_back(val);
		}
		void pop()
		{
			_con.pop_back();
		}
		void swap(stack& st)
		{
			_con.swap(st._con);
		}

	private:
		container _con;
	};
}

3.3 queue的模拟实现

namespace lbz
{
	template<class T, class container = deque<T>>
	class queue
	{
	public:
		queue(const container& ctnr = container())
			:_con(ctnr)
		{}
		bool empty() const
		{
			return _con.empty();
		}
		size_t size() const
		{
			return _con.size();
		}
		T& front()
		{
			return _con.front();
		}
		const T& front() const
		{
			return _con.front();
		}
		T& back()
		{
			return _con.back();
		}
		const T& back() const
		{
			return _con.back();
		}
		void push(const T& val)
		{
			_con.push_back(val);
		}
		void pop()
		{
			_con.pop_front();
		}
		void swap(queue& st)
		{
			_con.swap(st._con);
		}

	private:
		container _con;
	};
}

4. deque的简单介绍

一般来说,stack底层适合用vector,queue底层适合用list,但是在标准库中,二者都用deque作为底层容器的缺省参数。

vector和list由于自身的优缺点过于明显且对立,所以二者在实现时因为效率的原因,各自有一些对方没有的接口,这也使得他们分别与stack和list绑定。

而deque可以说是vector和list的缝合怪,因为二者的接口它都支持,所以可以同时作为stack和queue的底层容器,这也就意味着它是将二者优缺点折中的数据结构。

4.1 deque的结构

deque(双端队列)是一种双开口的"连续"空间的数据结构。

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

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

 其中,map是一个指针数组,存储各个存储数据的数组的地址。

初始的数组存放在map中部位置,当需要向前扩容时,就在将新申请的数组放到已有数组在map中的前一个位置;当需要向后扩容时就将新申请的数组放到map中已有数组的后一个位置。

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

deque迭代器具体是如何运作的比较复杂,在此不作过多论述,简单了解即可。 

4.2 deque的优缺点

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

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

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

4.3 为什么选择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的优点,而完美的避开了其缺陷。

5. 优先级队列priority_queue

顾名思义,优先级队列就是让队中的数据按照优先级(大小顺序等)排队出队。

priority_queue的底层实际上就是堆,既可以依据大小来建大堆小堆,也可以根据自定义的逻辑来制定优先级。

函数指针在C++中并不推荐,这里我们要用到仿函数来传递我们的建堆逻辑。

5.1 仿函数

STL库中priority_queue的原型如下:

template <class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;

我们可以看到,模板的类型参数列表中有一个Compare类型,其缺省值为less<typename Container::value_type>。

这里的less是一个特殊的类(对应的有greater类),它没有任何成员变量,但是对"()"进行了重载:

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

于是,less类就可以这样来使用:

int main()
{
    int a = 10, b = 10;

    less<int> isLess;
    if(isLess(a, b))
    cout << "a < b" << endl;

    return 0;
}

这里,由less<int>类实例化出的对象isLess有了类似函数的用法,效果也与函数相同,这就是所谓的仿函数。

通过这样的仿函数,我们就可以将自定义的逻辑传入到类模板中去。

注意,传入"less",建的是大堆;传入"greater",建的是小堆

5.2 priority_queue的常用接口

由于priority_queue实质上就是堆,所以下表中均以堆代指。

bool empty() const;判断堆是否为空(空true/非空false)
size_t size() const;返回堆中的数据个数
const T& top() const;返回堆队头元素(堆顶元素)
void push(const T& val);入堆
void pop();出堆
void swap(priority_queue& x);交换两个堆的内容

5.3 priority_queue的模拟实现

namespace lbz
{
	template <class T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
			AdjustUp(_con.size() - 1);
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		const T& top()
		{
			return _con[0];
		}
		size_t size() const
		{
			return _con.size();
		}
		bool empty() const
		{
			return _con.empty();
		}

	private:
		void AdjustUp(size_t child)
		{
			size_t parent = (child - 1) / 2;
			while (child > 0 && _com(_con[parent], _con[child]))
			{
				swap(_con[parent], _con[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
		}

		void AdjustDown(size_t parent)
		{
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _com(_con[child], _con[child + 1]))
					child++;

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

		Container _con;
		Compare _com;
	};
}

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

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

相关文章

Kafka日志索引详解与常见问题分析

目录 一、Kafka的Log日志梳理 1、Topic下的消息是如何存储的&#xff1f; 1. log文件追加记录所有消息 2. index和timeindex加速读取log消息日志 2、文件清理机制 1. 如何判断哪些日志文件过期了 2. 过期的日志文件如何处理 3、Kafka的文件高效读写机制 1. Kafka的文件…

刷题日记【160. 相交链表】

160. 相交链表 这虽然是道简单题&#xff0c;但是最简单的方法&#xff08;Set数组存一边然后另一边遍历判断当前结点是否存在于另一边&#xff09;性能很一般&#xff0c;可以思考用双指针来优化写法&#xff08;可以将空间复杂度降至 O(1)&#xff09; 捋思路时&#xff0c;…

Maya怎么把黑色的面反转为白色面

1、选中需要调整的面。 2、点击菜单栏中的“网格显示”&#xff0c;再点击点击“反转(Reverse)”。 3、反转后&#xff0c;原本黑色的面将会变成正常的面&#xff0c;法线方向也会相应改变。 按住ctrlshift鼠标中键 拖动快捷图标至工具栏

NullPointerException 是什么, 如何修复?

下面是chatGPT 01的回复&#xff1a; **NullPointerException**&#xff08;空指针异常&#xff09;是在 Java 等编程语言中出现的运行时错误&#xff0c;当你尝试使用一个尚未初始化的对象引用&#xff08;即&#xff0c;指向 null&#xff09;时就会发生。这个异常表示你的程…

金融教育进乡村:红土散户联盟教你如何分辨好坏资产

2024年8月&#xff0c;【红土散户联盟】再度将关注的目光投向了农村地区&#xff0c;特别是那些经济不发达的地区。作为一个致力于为社会带来积极改变的组织&#xff0c;红土散户联盟再次举办了农村理财讲座&#xff0c;旨在帮助这些地区的居民提高他们的财务管理能力和投资意识…

OJ题-反转链表

给你一个单链表的头节点&#xff0c;请反转链表&#xff0c;并返回新的链表 eg&#xff1a; 1,2,3,4,5--->5,4,3,2,1 //反转链表 struct ListNode* reverseList(struct ListNode* head) {//定义三个变量struct ListNode* n1, * n2, * n3;n1 NULL;n2 head;n3 head->n…

图解Self-Attention和代码实现,大语言模型基础思维导图

文章目录 1 Self-Attention的概念注意优缺点 2 Self-Attention的原理Q,K,V, and Self-Attention计算公式代码实现 Self-Attention的计算细节输入是如何Embedding的&#xff1f;Word EmbeddingsSentence EmbeddingsPre-trained Embeddings SelfAttention是如何计算的计算图 4 Se…

为什么说开放式耳机比入耳式的好?学生党必入的蓝牙耳机推荐

因为开放式耳机相比入耳式耳机更具优势&#xff0c;具体如下&#xff1a; 佩戴舒适度更高&#xff1a; 开放式耳机通常不需要插入耳道&#xff0c;不会对耳道产生压迫&#xff0c;长时间佩戴耳朵不易感到闷热、疼痛或不适&#xff0c;减少了对耳部的物理压迫和摩擦&#xff0…

单硬盘安装Win10和麒麟V10双系统指导建议

随着信创电脑的普及,国产操作系统也逐渐走进了大家的视野,许多人选择了国产操作系统来体验其开源、安全、高效的特性,而Windows系统也是大多数人习惯使用的操作系统。一台电脑上同时安装银河麒麟V10和Windiows10双系统也成为了非常常见的需求。那么,如何在一台电脑上安装银…

阿里员工爆料:阿里前CTO张建峰,也是中专毕业的!之前觉得大专生做到 P9已经很牛了,现在看来,原来中专生也能成为合伙人

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 《Java代码审…

AI大模型在知识管理平台上的应用:泛微·采知连实现自动采集.精准搜索.智能问答.主动推荐

AI技术的发展&#xff0c;正在推动组织知识管理模式发生变革。知识管理系统通过各种应用实现知识体系落地&#xff0c;当前聚焦于整合生成式AI技术&#xff0c;以提升业务效率。 组织在数字化进程中面临着知识增量增多、知识更新频率变快、知识与业务结合更紧密等挑战&#xff…

三种mybatis表的列名和对象属性名不一致处理方法

目录 三种mybatis表的列名和对象属性名不一致处理方法 1.使用 resultMap 映射 1&#xff09;mapper 2&#xff09;mapper.xml 3&#xff09;测试代码 4&#xff09;测试结果 ​编辑 2.使用别名 1&#xff09;mapper 2&#xff09;mapper.xml 3&#xff09;测试代码 4&#xff0…

浅谈vue2.0与vue3.0的区别(整理十六点)

目录 1. 实现数据响应式的原理不同 2. 生命周期不同 3. vue 2.0 采用了 option 选项式 API&#xff0c;vue 3.0 采用了 composition 组合式 API 4. 新特性编译宏 5. 父子组件间双向数据绑定 v-model 不同 6. v-for 和 v-if 优先级不同 7. 使用的 diff 算法不同 8. 兄弟组…

浅谈住房城乡建设部科技创新平台布局重点方向

最近住房建设部组织开展住房城乡建设部科技创新平台&#xff08;以下简称部科技创新平台&#xff09;申报工作。详细内容见住房城乡建设部科技创新平台开始申报了 (qq.com)。在这里有4大方向共15个课题。内容见下图&#xff1a; 虽然我是做技术的&#xff0c;但是如何体现创新还…

Linux基础3-基础工具3(make,makefile,gdb详解)

上篇文章&#xff1a;Linux基础3-基础工具2&#xff08;vim详解&#xff0c;gcc详解&#xff09;-CSDN博客 本章重点&#xff1a; 1.自动化构建工具make,makefile 2.linux调试工具gdb 目录 一. 自动化构建工具make,makefile 1.1 make使用 1.2 使用make注意点 a. make和文件时…

【演化博弈论】:双方演化博弈的原理与过程

目录 一、演化博弈的原理1. 基本概念2. 参与者的策略3.演化过程 二、MATLAB 代码解读&#xff08;博弈参与主体&#xff08;双方&#xff09;策略选择的动态演化讨程&#xff09;三、MATLAB 代码解读&#xff08;博弈主体随着时间策略选择的动态演化讨程&#xff09;四、结论 演…

Mac 上,终端如何开启 proxy

前提 确保你的浏览器可以访问 google&#xff0c;就是得先有这个能力 步骤 查看网络的 http/https 还有 socks5 的 port配置 .zshrc 查看 port 点击 wifi 设置 以我的为例&#xff0c;我的 http/https 都是 7890&#xff0c; socks5 是 7891 查看代理的port 以我的软件…

linux网络编程1

24.9.16学习目录 一.TCP/IP协议简介1.TCP/IP的分层结构2.协议的简介 二、MAC地址和IP地址1.网卡2.MAC地址3.IP地址&#xff08;1&#xff09;IP地址的分类&#xff08;2&#xff09;IP地址的特点&#xff08;3&#xff09;回环IP地址 3.子网掩码4.端口&#xff08;1&#xff09…

【C语言】分支和循环专题应用

分支和循环专题应用 1、随机数生成1.1rand1.2 srand函数介绍1.3 time函数介绍1.4 设置随机数的范围 2、猜数字游戏的代码及实现 通过了分支和循环的介绍学习之后&#xff0c;我们可以运用分支和循环语句写出一些有趣的代码了&#xff0c;让我们来一起探索吧&#xff01; 写一个…

‌PhotoZoom Pro 9‌和‌PhotoZoom Classic 9‌都提供了多项新功能

​PhotoZoom 9是一款划时代的、技术上产生革命性影响的数码图片放大工具。该软件使用了全新的S-Spline技术&#xff08;拥有自动调节、领先的差值算法等技术及亮点&#xff09;&#xff0c; 开创了图片放大技术的新领域&#xff0c;采用更为领先的优化算法&#xff0c;对不断放…