【C++】了解设计模式,模拟实现栈和队列

news2024/12/23 19:22:57

文章目录

    • 一.设计模式
    • 二.stack的模拟实现
    • 三.queue的模拟实现
    • 四.了解deque
    • 五、题目练习

一.设计模式

设计模式有很多种,根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。

设计模式是是前辈们对代码开发经验的总结,是解决特定问题的一系列套路,比如适配器模式,迭代器模式

迭代器模式:迭代器封装后提供统一的访问方式,不暴露底层的细节,迭代器实际上是一种设计模式。

适配器模式:适配器实际上是一种转换,通过已有的东西封装转换出你想要的东西

而栈与队列可以通过适配器模式进行实现。

数组可以通过vector和list进行转换


二.stack的模拟实现

stack.h

#pragma once

#include <vector>
#include <list>
namespace hwc
{
	
	template<class T,class Container>
	//template<class T,class Container=vector<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

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

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

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

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

	private:
		Container _con;
	};
}

test.cpp

#include <iostream>
using namespace std;
#include "stack.h"
int main()
{
	//hwc::stack<int, vector<int>> st;
	hwc::stack<int,list<int>> st;
	//hwc::stack<int> st
	st.push(1);
	st.push(2);
	st.push(3);
	st.push(4);
	st.push(5);
	while (!st.empty())
	{
		cout << st.top() << endl;
		st.pop();
	}

	return 0;
}

三.queue的模拟实现

queue.h

#pragma once
#include <vector>
#include <list>
namespace hwc
{
	template<class T,class Container=list<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

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

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

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

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

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

	private:
		Container _con;
	};
}

test.cpp

#include <iostream>
using namespace std;
#include "Queue.h"
int main()
{
	hwc::queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);
	q.push(5);
	while (!q.empty())
	{
		cout << q.front() << endl;
		q.pop();
	}
	return 0;
}

四.了解deque

但是查看文档,Container发现用的并不是vector或是list,而是deque,申请空间做内存池

image-20230104103707021

image-20230104134934014

vector和list的结构都存在一些缺陷:vector真正的缺点在于扩容消耗,不支持头插头删,因为头部中部删除效率低。而list不支持随机访问,且CPU高速缓存命中率低,空间不连续。而deque兼具了vector和list的优点

image-20230104123733549

int main()
{
	deque<int> d;
	d.push_back(1);
	d.push_back(2);
	d.push_back(3);
	d.push_back(4);

	d.push_front(10);
	d.push_front(20);

	for (size_t i = 0; i < d.size(); i++)
	{
		cout << d[i] << " ";
	}
	cout << endl;
	return 0;
}

image-20230104130106858

底层实现:deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的 ,为了避免扩容,由多个buffer数组构成,确定中控指针数组

image-20230104132101944

并不是说deque就是一定很完美,deque真正的问题在于随机访问的支持:算在第几个buffer在算在这个buffer的第几个。下标的随机访问有一定的消耗,没有vector的随机访问快。中间插入删除也有一定的消耗,相比list中间插入删除不够灵活,没有list快。

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

deque作为stack和queue的底层默认容器 :在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高


五、题目练习

最小栈

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。

示例 1:

输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

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();
    }
    stack<int> st;
    stack<int> minst;
};

栈的压入、弹出序列

描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

  1. 0<=pushV.length == popV.length <=1000

  2. -1000<=pushV[i]<=1000

  3. pushV 的所有数字均不相同

示例1

输入:

[1,2,3,4,5],[4,5,3,2,1]

返回值:

true

说明:

可以通过push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop()
这样的顺序得到[4,5,3,2,1]这个序列,返回true      

示例2

输入:

[1,2,3,4,5],[4,3,5,1,2]

返回值:

false

说明:

由于是[1,2,3,4,5]的压入顺序,[4,3,5,1,2]的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false      
class Solution {
public:
    bool IsPopOrder(vector<int> pushV,vector<int> popV) {
        stack<int> ST;
        
        for(int i = 0,j=0;i<pushV.size();i++)
        {
            ST.push(pushV[i]);
            while(!ST.empty()&&ST.top()==popV[j])
            {
                ST.pop();
                j++;
            }
        }
        return ST.empty();
    }
};

逆波兰表达式求值

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用 32 位 整数表示。

示例 1:

输入:tokens = [“2”,“1”,“+”,“3”,“*”]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:

输入:tokens = [“4”,“13”,“5”,“/”,“+”]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:

输入:tokens = [“10”,“6”,“9”,“3”,“+”,“-11”,““,”/“,””,“17”,“+”,“5”,“+”]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(int i = 0;i<tokens.size();i++)
        {
            if(tokens[i]=="+"||tokens[i]=="-"||tokens[i]=="*"||tokens[i]=="/")
            {
                long long num1 = st.top();
                st.pop();
                long long num2 = st.top();
                st.pop(); 
                if(tokens[i]=="+") st.push(num2+num1);
                if(tokens[i]=="-") st.push(num2-num1);
                if(tokens[i]=="*") st.push(num2*num1);
                if(tokens[i]=="/") st.push(num2/num1);
            }
            else
            {
                st.push(stoi(tokens[i]));
            }
        }
        return st.top();
    }
};

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

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

相关文章

SpringBoot+VUE前后端分离项目学习笔记 - 【11 SpringBoot代码生成器_MybatisPlus】

引入mp生成器依赖 pom.xml <!-- 代码生成器 --> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1</version> </dependency> <dependency><grou…

RabbitMQ 单机安装-CentOS

RabbitMQ 单机安装-CentOS 官网查看RabbitMQ和对应的Erlang版本 进入 RabbitMQ 官网 &#xff0c;点击 顶上的 Get Started 点击Download Installation 点击左侧的Erlang Versions 查看对应版本 根据自己需要安装的RabbitMQ版本&#xff0c;找到需要Erlang的版本。 下…

什么是Spring Cloud?Spring Cloud介绍

简介Spring Cloud项目的官方网址&#xff1a;https://projects.spring.io/spring-cloud/Spring Cloud 并不是一个项目&#xff0c;而是一组项目的集合。在 Spring Cloud中包含了很多的子项目&#xff0c;每一个子项目都是一种微服务开发过程中遇到的问题的一种解决方案。它利用…

红中群内每日分享题目解析——第一天

目录 题目一&#xff1a;最后一次用的英雄 题目二&#xff1a;狗哥去哪 摩斯密码 题目三&#xff1a;黑丝白丝还有什么丝 题目一&#xff1a;最后一次用的英雄 ​​​​​​ 感谢我徒弟(不是 告知并解出此题 杭椒的博客_CSDN博客-网安经验分享,网络安全工具,CISP-PTE备考笔记…

音视频开发常用工具

目录 1.VLC播放器简介 1.1 VLC 播放器 1.2 VLC的功能列表 1.3 VLC播放网络串流 1.4 VLC作为流媒体服务器 2. MediaInfo简介 2.1 MediaInfo 2.1.1 获取多媒体文件信息 2.1.2 支持的格式 2.1.3 .查看方式 2.1.4 国际化 2.2 MediaInfo使用方法 2.3 MediaInfo参数说明 3…

将powershell、cmd和vscode终端的编码永久修改成utf-8

powershell修改方法 1、以管理员身份打开powershe New-Item $PROFILE -ItemType File -Force 2、打开C盘&#xff0c;找到我的文档中的WindowsPowerShell文件夹 3、编辑这个ps1文件&#xff08;默认是空的&#xff09;&#xff0c;加上以下代码 $OutputEncoding [console…

Handler的消息机制与消息延迟代码实现

Handler的作用 Handler消息机制在Android中的应用非常广泛&#xff0c;很多组件的底层实现都是靠Handler来完成的&#xff0c;所以掌握Handler消息机制的原理还是非常重要的。Handler的主要功能有两点&#xff1a; 1.它可以在不同的线程之间传递消息 我们都知道Andorid中规定…

menuconfig与Kconfig入门学习

概述 menuconfig是Linux平台用于管理代码工程、模块及功能的实用工具。 menuconfig的使用方式通常是在编译系统之前在系统源代码根目录下执行make menuconfig命令从而打开一个图形化配置界面&#xff0c;再通过对各项的值按需配置从而达到影响系统编译结果的目的。 Nuttx的m…

新建一个SpringMVC项目

步骤1:创建Maven项目 打开IDEA,创建一个新的web项目 步骤2:补全目录结构 因为使用骨架创建的项目结构不完整&#xff0c;需要手动补全 步骤3:导入jar包 将pom.xml中多余的内容删除掉&#xff0c;再添加SpringMVC需要的依赖 <?xml version"1.0" encoding"UT…

一文读懂远程线程注入

在红队行动中&#xff0c;红队的目的都是要在不暴露自身行动的前提下&#xff0c;向蓝队发动攻击。他们使用各种技术和程序来隐藏C2连接和数据流。攻击活动的第一步是获得初始访问权。他们会使用定制的恶意软件和有效载荷来躲避防杀软和EDR等防御工具。 在这些典型的攻击活动中…

代码中大量爆红,IDE设置jdk版本,及设置后无效的解决

代码大量爆红&#xff0c;检查ide设置的jdk版本是否与项目的java版本一致&#xff0c;做以下配置 1.project structrue 2.maven 3.如果以上设置以后没有效果&#xff0c;就把项目中的.ide文件删除&#xff0c;并且pom里面加上下面的代码&#xff0c;重启IDE。 <build&g…

植物大战僵尸:植物栏无冷却的找法

通过使用CE遍历内存&#xff0c;定位到植物无冷却的核心代码处&#xff0c;并通过修改关键的跳转来实现所有植物的无冷却。 第一种遍历修改方式 无冷却的遍历技巧1&#xff1a; 打开CE->回到游戏种植一颗向日葵 -> 扫描未知初始数值然后切回游戏 ->马上切回CE ->…

< JavaScript技术分享: 大文件切片上传 及 断点续传思路 >

文章目录&#x1f449; 前言及含义切片上传断点续传&#x1f449; 一、实现思路&#x1f449; 二、使用场景&#x1f449; 参考文献&#x1f449; 伸手党福利&#xff1a; 即拿即用&#xff08;前/后端思路均有&#xff09;往期内容 &#x1f4a8;&#x1f449; 前言及含义 在…

MVVM学习

mvvm基础知识Model&#xff1a;repository Entity 数据库 网络访问等对数据进行直接操作的代码View:视图代码 xml activity fragment adapter 等ui层的一些代码ViewModel:视图模型 用来和View ,Model层交互&#xff0c;将Model层的数据显示到View上&#xff0c;并处理View层的事…

Android Softap Mac地址随机化

目录 Android Q以后 Android Q 获取方式 Android Q以后 在SoftapManager创建的时候,可以跟下 Android Q Q:WifiManager#getWifiApConfiguration()只能获取到SSID和pwd等,无法获取到对应的BSSID。 原因分析:Android Q Softap的MAC地址设置是在HAL层,并没有给Framework赋…

result.isAck()报错

IDEA 的 bug&#xff0c;两张图解释

图像平滑处理

平滑处理是指在某些数学和统计分析中&#xff0c;对于原始数据做出拟合&#xff0c;并生成更平滑的数据。这通常是为了抑制原始数据中的离群点或噪声&#xff0c;使得数据更具可读性和可解释性。 在图像处理中常用于模糊处理和降低噪声。平滑滤波器使用给定邻域内的像素平均灰度…

Word处理控件Aspose.Words功能演示:在 C# .NET 中将 Word 转换为 PDF - 完整指南

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

JS面试题--JavaScript基础

1. new操作符的实现原理 创建一个对象将构造函数的作用域赋给新对象&#xff08;也就是将对象的__proto__属性指向构造函数的prototype属性&#xff09;指向构造函数中的代码&#xff0c;构造函数中的this指向该对象&#xff08;也就是为这个对象添加属性和方法&#xff09;返…

Arthas tt -t , tt -i 以及 watch相关使用理解 解析入参和结果等

Arthas tt 系列作用 方法执行数据的时空隧道&#xff0c;记录下指定方法每次调用的入参和返回信息&#xff0c;并能对这些不同的时间下调用进行观测 一、 tt -t 作用和一般用法 tt -t 一般用于监控某一个方法的调用记录&#xff0c;如果调用多此会出现多条记录&#xff0c;一…