41 stack类与queue类

news2024/12/28 7:16:18

目录

一、简介

(一)stack类

(二)queue类

二、使用与模拟实现

(一)stack类

1、使用

2、OJ题

(1)最小栈

(2)栈的弹出压入序列

(3)逆波兰表达式求值

3、模拟实现

(二)queue类

1、使用

2、模拟实现

三、priority_queue类

(一)使用

(二)OJ题

1、数组中第K个大的元素

(三)模拟实现

四、deque类(了解)

(一)原理介绍

(二)deque缺陷

(三)做底层默认容器


一、简介

(一)stack类

        在 C++ 中,stack(栈)类是一个容器适配器,它被定义在<stack>头文件中。容器适配器是一种设计模式(就是把解决问题的经验总结出来),它允许将一个已有的容器类(如 vector、list或deque)转换为另一种容器类型的接口stack的情况下,以其他类型的容器为基础,并与提供栈这种数据结构相关操作的接口进行封装。具体声明如下:

        类模板第一个参数是类型,第二个参数为适配器,默认为deque。

(二)queue类

        在 C++ 中,queue(队列)类也是一个容器适配器,它提供了队列这种数据结构的功能。它被定义在<queue>头文件中,像stack一样,queue也是基于其他底层容器(如dequelist)来实现的,默认底层容器是deque。具体声明如下:

        类模板第一个参数是类型,第二个参数为适配器,默认为deque。

二、使用与模拟实现

        stack类与queue类的接口比较简单,因为要保证数据的后进先出/先进先出的属性,所以不提供迭代器。(设计迭代器的话就可以遍历到栈中的每一个元素,就能进行随机插入与删除,就违反了后进先出/先进先出的理念)

(一)stack类

1、使用

        stack类本质是实现栈的功能,即具备先进后出的属性与相关接口,具体如下:

       

函数说明接口说明
stack()
构造空的栈
empty()
检测 stack 是否为空
size()
返回 stack 中元素的个数
top()
返回栈顶元素的引用
push()
将元素 val 压入 stack
pop()
stack 中尾部的元素弹出

        示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stack>
#include<queue>
#include<vector>
using namespace std;

void test01()
{
	stack<int, vector<int>> st;
	for (size_t i = 1; i < 5; i++)
	{
		st.push(i);
	}
	
	cout << "empty():" << st.empty() << endl;
	cout << "size():" << st.size() << endl;

	//遍历
	cout << "遍历:" << endl;
	for (size_t i = 1; i < 5; i++)
	{
		cout << st.top() << " ";
		st.pop();
	}
	cout << endl;
}

int main()
{
	test01();

	return 0;
}

        结果如下:

        注意:栈的遍历只能是一边出一边看。

2、OJ题

(1)最小栈

        思路:创建一个正常的栈和一个值插入比自己头元素小的值的栈(为空要插入一次)。

        代码如下:

class MinStack {
public:
    //该构造不写也行,用系统自动生成的无参构造
    MinStack() {
        //无参构造中的自定义类型会在初始化列表调用他们自己的构造
    }
    
    void push(int val) {
        _st.push(val);
        if(_minst.empty() || val <= _minst.top())
            _minst.push(val);
    }
    
    void pop() {
        if(_st.top() == _minst.top())
            _minst.pop();
        _st.pop();
    }
    
    int top() {
        return _st.top();
    }
    
    int getMin() {
        return _minst.top();
    }

private:
    stack<int> _st;
    stack<int> _minst;
};

(2)栈的弹出压入序列

        解题思路:

        ① 创建一个栈;

        ② 给入栈数组给一个指针pushi,先入栈pushi位置的数据,pushi++;

        ③ 给出栈数组给一个指针popi,栈顶数据跟popi位置序列数据比较,若果匹配则出栈,popi++,回到第二步;不匹配或栈为空则回到第一步。(不匹配是后面才出栈);

        ④ 到了最后,pushi等于pushV.size(),若全部匹配,则栈是空的,判空的结果与返回的结果一致;若不匹配则栈不为空。

        代码如下:

bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
        size_t pushi = 0;
        size_t popi = 0;
        stack<int> st;

        while(pushV.size()>pushi)
        {
            st.push(pushV[pushi++]);

            while(!st.empty() && popV[popi] == st.top())
            {
                st.pop();
                popi++;
            }
        }

        return st.empty();
    }

(3)逆波兰表达式求值

        解题思路:

        ① 操作数入栈;

        ② 操作符,取栈顶两个操作数运算,运算结果继续入栈;(先取出来的是右操作数,因为栈的先进后出原则)

        ③ 最后一个栈元素就是运算结果。

        代码如下:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
         stack<int> s;
         for(auto& str : tokens)
         {
            if(!(str=="+" || str=="-" || str=="*" || str=="/"))
            {
                //操作数则入栈
                s.push(stoi(str));
            }
            else
            {
                //操作符,取栈顶两元素进行运算
                int right = s.top();
                s.pop();
                int left = s.top();
                s.pop();
                switch(str[0])
                {
                case '+':
                    s.push(left + right);
                    break;
                case '-':
                    s.push(left - right);
                    break;
                case '*':
                    s.push(left * right);
                    break;
                case '/':
                    s.push(left / right);
                    break;
                }
            }
         }
         return s.top();
    }
};

        注意:

        ① 操作符是一个字符,从string里取出来的操作符在case中要用' '进行引用。

        ② 因为操作数可能是多位或者是负数,所以使用中括号加下标的形式进行使用操作数的话可能要把他们拼在一起,非常麻烦,所以直接使用stoi把string匿名对象的内容直接转为整形。

3、模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<deque>

namespace zyb 
{
	template<class T, class Container = deque<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() const // 指向的内容不能修改
		{
			return _con.size();
		}

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

	private:
		Container _con;//使用其他结构来作为栈的结构
	};
}

       测试代码如下:

void test01()
{
	zyb::stack<int> st;
	for (size_t i = 0; i < 5; i++)
	{
		st.push(i);
	}

	cout << "size():" << st.size() << endl;
	cout << "top():" << st.top() << endl;
	cout << "empty():" << st.empty() << endl;

	while (!st.empty())
	{
		cout << st.top() << " ";
		st.pop();
	}
}

        结果如下所示:

        注意:把传入容器的尾部当做栈顶,对栈进行插入删除操作时直接push_back与pop_back即可,若要获取栈顶元素,使用back()这种通用型接口即可获得栈顶元素。

(二)queue类

1、使用

        queue类本质是实现队列的功能,即具备先进先出的属性与相关接口,具体如下:

        示例代码如下:

void test02()
{
	queue<int> q;
	for (size_t i = 1; i < 5; i++)
	{
		q.push(i);
	}

	cout << "empty():" << q.empty() << endl;
	cout << "size():" << q.size() << endl;
	cout << "front():" << q.front() << endl;
	cout << "back():" << q.back() << endl;

	//遍历
	cout << "遍历:" << endl;
	for (size_t i = 1; i < 5; i++)
	{
		cout << q.front() << " ";
		q.pop();
	}
	cout << endl;
}

int main()
{
	test02();

	return 0;
}

        运行结果如下:

2、模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<deque>

namespace zyb
{
	template<class T, class Container = deque<T>>//半缺省模板参数
	class queue
	{
	public:
		//不用接构造函数,因为会调用其他结构的默认构造

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

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

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

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

		size_t size() const // 指向的内容不能修改
		{
			return _con.size();
		}

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

	private:
		Container _con;//使用其他结构来作为栈的结构
	};
}

       测试代码如下:

void test02()
{
	zyb::queue<int> q;
	for (size_t i = 0; i < 5; i++)
	{
		q.push(i);
	}

	cout << "size():" << q.size() << endl;
	cout << "front():" << q.front() << endl;
	cout << "back():" << q.back() << endl;
	cout << "empty():" << q.empty() << endl;

	while (!q.empty())
	{
		cout << q.front() << " ";
		q.pop();
	}
}

        结果如下所示:

        注意:

        ① 队列不能使用vector进行适配,因为vector不支持头删。

        ② 在测试类中要先展开命名空间再引入stack或queue类模板(里面没展开命名空间std),因为头文件展开后找不到展开的命名空间std,deque找不到出处,会报错。(在vs中写在头文件下面也没事,因为头文件处只是一个类模板,没有实例化,在编译时只做简单的检测,在进行业务处理的时候进行实例化再检查,发现已有命名空间,所以不会报错)

三、priority_queue类

        在 C++ 中,priority_queue(优先队列,)类是一种特殊的队列容器适配器根据严格的弱排序标准(less),它的第一个元素总是它所包含的元素中最大的。

        类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素,默认大根堆)。具体声明如下:

(一)使用

        优先级队列默认使用vector作为其底层存储数据的容器在vector上又使用了堆算法将vector中元素构造成堆的结构因此priority_queue就是堆所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。

函数声明
接口说明
priority_queue()
priority_queue(first, last)
构造一个空的优先级队列
empty( )
检测优先级队列是否为空,是返回 true ,否 则返回 false
top( )
返回优先级队列中最大 ( 最小元素 ) ,即堆顶元
push(x)
在优先级队列中插入元素 x
pop()
删除优先级队列中最大 ( 最小 ) 元素,即堆顶元

(二)OJ题

1、数组中第K个大的元素

        解题思路:根据优先队列的特性,pop出来的都是最大的元素,则可先pop前k-1个元素,最后再取top元素。

        代码如下:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        //建堆(默认大堆,出的也是大的数字)
        priority_queue<int> p(nums.begin(), nums.end());

        while(--k)
        {
            p.pop();//把前k-1个最大的数删掉
        }

        return p.top();//在栈顶的就是最大的那个
    }
};

(三)模拟实现

#pragma once
#include<iostream>
#include<vector>

namespace zyb
{
	template<class T, class Container = vector<T>>
	class priority_queue//小堆
	{
	public:
		void AdjustUp(int child)
		{
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (_con[child] < _con[parent])
				{
					swap(_con[child], _con[parent]);
					child = parent;//更新下标
					parent = (parent - 1) / 2;
				}
				else
					break;
			}
		}

		void push(const T& x)//插入一个数据后要使用向上调整算法佬保持堆的结构
		{
			_con.push_back(x);//尾插数据

			//进行向上调整算法
			AdjustUp(_con.size() - 1);
		}

		void AdjustDowm(int parent)
		{
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child] > _con[child + 1])//孩子节点对比,选出较小的一个(因为是小根堆,父亲节点要小)
					child++;
				if (_con[parent] > _con[child])//交换父节点与孩子节点,移动节点位置
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
					break;
			}
		}
			
		void pop()//删除,先把堆顶元素与堆尾元素交换,删除堆尾元素,再进行向下调整算法
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			AdjustDowm(0);
		}

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

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

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

	private:
		Container _con;
	};
}

        测试代码如下:

void test03()
{
	zyb::priority_queue<int> pq;//小堆
	for (size_t i = 0; i < 5; i++)
	{
		pq.push(i);
	}

	cout << "size():" << pq.size() << endl;
	cout << "top():" << pq.top() << endl;
	cout << "empty():" << pq.empty() << endl;

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop(); 
	}
}

        其结果为:

四、deque类(了解)

(一)原理介绍

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

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

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

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

(二)deque缺陷

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

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

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

(三)做底层默认容器

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

        ① stack和queue不需要遍历(stack和queue没有迭代器,避开了deque的缺点),只需要在固定的一端或者两端进行操作。

        ② 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的 元素增长时,deque不仅效率高,而且内存使用率高。

        结合了deque的优点,而完美的避开了其缺陷。

 


        以上内容仅供分享,若有错误,请多指正。

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

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

相关文章

wangEditor富文本插件在vue项目中使用和媒体上传的实现

wangEditor是前端一个比较流行的简洁易用&#xff0c;功能强大的前端富文本编辑器&#xff0c;支持 JS Vue React&#xff0c;提供了很多丰富的功能&#xff0c;下面手把手教你实现wangWditor富文本插件在vue项目中配置&#xff0c;保存、图片上传等功能。无脑ctrlc即可 基本功…

VMwareTools安装(ubuntu23)

1.打开VMware&#xff0c;菜单栏虚拟机->安装VMwareTools 2.点开光驱&#xff0c;把压缩包复制到桌面 3.解压 如何开启sudo权限&#xff1a; sudo passwd root 之后输入密码查看解压文件夹&#xff0c;执行vmware-install.pl文件 安装过程中碰见如下报错信息&#xff1a;…

jangow-01-1.0.1靶机

靶机 ip&#xff1a;192.168.152.155 把靶机的网络模式调成和攻击机kali一样的网络模式&#xff0c;我的kali是NAT模式, 在系统启动时(长按shift键)直到显示以下界面 ,我们选第二个&#xff0c;按回车。 继续选择第二个&#xff0c;这次按 e 进入编辑页面 接下来&#xff0c;…

C# GDI+数码管数字控件

调用方法 int zhi 15;private void button1_Click(object sender, EventArgs e){if (zhi > 19){zhi 0;}lcdDisplayControl1.DisplayText zhi.ToString();} 运行效果 控件代码 using System; using System.Collections.Generic; using System.Drawing.Drawing2D; using …

Cilium:BPF 和 XDP 参考指南(2021)

大家觉得有意义和帮助记得及时关注和点赞!!! BPF 是 Linux 内核中一个非常灵活与高效的类虚拟机&#xff08;virtual machine-like&#xff09;组件&#xff0c; 能够在许多内核 hook 点安全地执行字节码&#xff08;bytecode &#xff09;。很多 内核子系统都已经使用了 BPF&a…

LabVIEW条件配置对话框

条件配置对话框&#xff08;Configure Condition Dialog Box&#xff09; 要求&#xff1a;Base Development System 当右键单击**条件禁用结构&#xff08;Conditional Disable Structure&#xff09;**并选择以下选项时&#xff0c;会显示此对话框&#xff1a; Add Subdiagr…

机器学习-高斯混合模型

文章目录 高斯混合模型对无标签的数据集&#xff1a;使用高斯混合模型进行聚类对有标签的数据集&#xff1a;使用高斯混合模型进行分类总结 高斯混合模型 对无标签的数据集&#xff1a;使用高斯混合模型进行聚类 对有标签的数据集&#xff1a;使用高斯混合模型进行分类 总结

GitLab 服务变更提醒:中国大陆、澳门和香港用户停止提供服务(GitLab 服务停止)

目录 前言 一. 变更详情 1. 停止服务区域 2. 邮件通知 3. 新的服务提供商 4. 关键日期 5. 行动建议 二. 迁移指南 三. 注意事项 四. 相关推荐 前言 近期&#xff0c;许多位于中国大陆、澳门和香港的 GitLab 用户收到了一封来自 GitLab 官方的重要通知。根据这封邮件…

MacOS下TestHubo安装配置指南

TestHubo是一款开源免费的测试管理工具&#xff0c; 下面介绍MacOS私有部署的安装与配置。TestHubo 私有部署版本更适合有严格数据安全要求的企业&#xff0c;支持在本地或专属服务器上运行&#xff0c;以实现对数据和系统的完全控制。 1、Mac 服务端安装 Mac安装包下载地址&a…

css绘制圆并绘制圆的半径

<div class"item1"></div>.item1 {position: relative;width: 420px;height: 420px;border-radius: 50%; /* 圆形 */color: white; /* 文本颜色 */background-color: rgba(154, 227, 36, 0.4); } .item1::before {content: "";position: absol…

【原理图专题】CIS库中有两部分组成的器件怎么查看符号库

在ICS库使用过程中&#xff0c;会遇到比如运放、MOS管等是由两个符号构成的一个器件。比如下图所示的器件&#xff1a; 为了方便我们知道内部结构&#xff0c;很可能把器件拆成两部分&#xff0c;一部分是PMOS&#xff0c;一部分是NMOS。包括大的MCU或芯片也是这样&#xff0c;…

HarmonyOS NEXT 实战之元服务:静态案例效果---查看国内航班服务

背景&#xff1a; 前几篇学习了元服务&#xff0c;后面几期就让我们开发简单的元服务吧&#xff0c;里面丰富的内容大家自己加&#xff0c;本期案例 仅供参考 先上本期效果图 &#xff0c;里面图片自行替换 效果图1完整代码案例如下&#xff1a; Index代码 import { authen…

ID读卡器TCP协议Delphi7小程序开发

Delphi 7是一款功能强大的快速应用程序开发工具&#xff0c;它提供了丰富的开发环境和组件库&#xff0c;支持多种操作系统和数据库连接&#xff0c;方便开发者进行高效的程序设计。然而&#xff0c;由于它是一款较旧的开发环境&#xff0c;在使用时需要注意兼容性和安全问题。…

C# 窗体应用程序嵌套web网页(基于谷歌浏览器内核)

有一个winform项目&#xff0c;需要借助一个web项目来显示&#xff0c;并且对web做一些操作,web页目是需要用谷歌内核&#xff0c;基于谷歌 Chromium项目的开源Web Browser控件来开发写了一个demo。 安装步骤 第一步&#xff1a;右键项目&#xff0c;点击 管理NuGet程序包 , 输…

SRA Toolkit简单使用(prefetch和fastq-dump)

工具下载网址&#xff1a; 01. 下载 SRA Toolkit ncbi/sra-tools 维基https://github.com/ncbi/sra-tools/wiki/01.-Downloading-SRA-Toolkit 我下载的是linux 3.0.10版&#xff0c;目前最新版如下&#xff1a;https://ftp-trace.ncbi.nlm.nih.gov/sra/sdk/3.1.1/sratoolkit.3…

Spring Boot介绍、入门案例、环境准备、POM文件解读

文章目录 1.Spring Boot(脚手架)2.微服务3.环境准备3.1创建SpringBoot项目3.2导入SpringBoot相关依赖3.3编写一个主程序&#xff1b;启动Spring Boot应用3.4编写相关的Controller、Service3.5运行主程序测试3.6简化部署 4.Hello World探究4.1POM文件4.1.1父项目4.1.2父项目的父…

【开源框架】从零到一:AutoGen Studio开源框架-UI层环境安装与智能体操作全攻略

一、什么是AutoGen AutoGen是微软推出的一款工具&#xff0c;旨在帮助开发者轻松创建基于大语言模型的复杂应用程序。在传统上&#xff0c;开发者需要具备设计、实施和优化工作流程的专业知识&#xff0c;而AutoGen则通过自动化这些流程&#xff0c;简化了搭建和优化的过程。 …

【论文阅读】MedCLIP: Contrastive Learning from Unpaired Medical Images and Text

【论文阅读】MedCLIP: Contrastive Learning from Unpaired Medical Images and Text 1.论文背景与动机2.MedCLIP的贡献3.提出的方法4.构建语义相似矩阵的过程5. 实验6. 结论与局限性 论文地址&#xff1a; pdf github地址&#xff1a;项目地址 Zifeng Wang, Zhenbang Wu, Di…

雷电模拟器安装Lxposed

雷电模拟器最新版支持Lxposed。记录一下安装过程 首先到官网下载并安装最新版&#xff0c;我安装的时候最新版是9.1.34.0&#xff0c;64位 然后开启root和系统文件读写 然后下载magisk-delta-6并安装 ,这个是吾爱破解论坛提供的&#xff0c;号称适配安卓7以上所有机型&#x…

全解:Redis RDB持久化和AOF持久化

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…