【C++】模拟实现栈和队列

目录

一.设计模式

二.stack的模拟实现

三.queue的模拟实现

四.deque的简单介绍(了解)

五.课后习题


在我们用C++模拟实现之前在C语言阶段的实现过的数据结构时,我们会想用更加舒服的方式写代码,这时我们就要用到设计模式

那么我们就要先了解一下什么是设计模式?

一.设计模式

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

比如适配器模式,迭代器模式

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

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

而我们今天要模拟实现的栈与队列就可以通过适配器模式进行实现

二.stack的模拟实现

关于栈的实现很简单,直接看代码,可以结合代码里标着的注释进行理解~

stack.h

#include<vector>
#include<list>
#include <deque>

namespace wyh
{
	//适配器           //传一个容器  这里用list
	template<class T, class Container = list<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()
		{
			_con.size();
		}

	private:
		Container _con;
	};

}

然后我们可以在test.cpp中测试一下:

三.queue的模拟实现

queue.h

namespace wyh
{
	//适配器              //传一个容器  这里用list 
	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()
		{
			_con.size();
		}

	private:
		Container _con;
	};
}

接下来我们对自己写的队列测试一下:

四.deque的简单介绍(了解)

上面我们用list实现了栈和队列,但是当我们查看文档时,发现Container用的并不是

vector或是list,而是deque

那么这个deque又是什么呢?

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

双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1)

它与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高

deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,由buffer数组构成,确定中控指针数组:

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

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

所以说,deque集成了vector和list的优点,它的出现本来是想取代vector和list,但是,正如大家所见,它并没有成功,否则我们学习的第一个容器也不会是vector或者list了

那么deque的缺陷又是什么呢?

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

而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构

最后一个问题:

为什么选择deque作为stack和queue的底层默认容器?

  1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端操作

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

五.课后习题

155.最小栈

. - 力扣(LeetCode)

参考代码:

class MinStack //因为这个函数 所以采用2个栈的实现方式
{
public:
    stack <int> s1;
    stack <int> s2;
    MinStack() 
    {}
    
    void push(int val) 
    {
       s1.push(val);
       if (s2.empty() || s1.top() <= s2.top()) //s2为空或者val值比s2中更小
            s2.push(s1.top());
    }
    
    void pop() 
    {
        if (s1.top() == s2.top()) //s1出栈元素与s2顶部相同
            s2.pop();
        
         s1.pop();
    }
    
    int top() 
    {
       return s1.top();
    }
    
    int getMin() 
    {
       return s2.top();
    }
};

栈的压入、弹出序列

栈的压入、弹出序列_牛客题霸_牛客网

参考代码

class Solution 
{
public:
    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) 
    {
        stack <int> s;

        int pushi = 0;//作为操控pushV这个压入栈的指针
        int popi = 0; //作为操控popV这个弹出栈的指针
        
        for(pushi = 0;pushi<pushV.size();pushi++)
        {
            //1.入一个pushV中的值
            s.push(pushV[pushi]);

            //2.s 跟popV 比较
            while(!s.empty() && s.top() == popV[popi])
            {
            //相等 出栈序列popV往后走 s的栈顶元素出
            ++popi;
            s.pop();
            }
            
            //不相等 出来 进行下一次循环
        }
        
        //s不为空,为false
        if (!s.empty()) return false;

        //s.empty()即s这个栈的所有数都出栈了,则为true
        else   return true;
    }
};

232.用栈实现队列

. - 力扣(LeetCode)

参考代码:

class MyQueue 
{
public:
   stack <int> s1;//用来返回队列的值
   stack <int> s2;

    MyQueue() //构造 不用写了
    {}
    
    void push(int x)  //push 是关键
    {
        while(!s1.empty())//x进来之前,先把s1的数全挪到s2去
        {
            s2.push(s1.top());
            s1.pop();
        }

      
        s1.push(x);//x入栈

       // 将s2中放着的s1的数全挪回来 如此可以保证用栈保持s1是队列的顺序
        while(!s2.empty())
        {
            s1.push(s2.top());
            s2.pop();
        }
    }
    
    int pop() //出队列 并返回这个出队的元素
    {
        int x = s1.top();
        s1.pop();
        return x;
    }
    
    int peek() ///返回对列的顶部元素
    {
      return s1.top();
    }
    
    bool empty() //判断队列是否为空
    {
     return s1.empty();
    }
};

225.用队列实现栈

. - 力扣(LeetCode)

参考代码:

class MyStack 
{
public:
    queue<int>q1;//返回值在q1实现
    queue<int>q2;

    MyStack() 
    {}
    
    void push(int x) 
    {
       q1.push(x);
    }
    
    int pop() //关键  使得我们用队列的结构模拟出栈的序列
    {
      //思路 : 出q1队列的元素到q2使得它只剩一个元素,再将这最后一个元素弹出
             // 当然了 当q1的元素弹出后 我们还要将q2的元素还原回q1
        
         int ans=q1.back();
        int n=q1.size()-1;
        while(n--)//出q1队列的元素到q2使得它只剩一个元素,再将这最后一个元素弹出就是我们要的
        {
            q2.push(q1.front());
            q1.pop();
        }
       
        q1.pop();

        //q1的元素弹出后 将q2的元素还原回q1
        while(!q2.empty())
        {
            q1.push(q2.front());
            q2.pop();
        }

        return ans;
    }
    
    int top() 
    {
     return q1.back();
    }
    
    bool empty() 
    {
        return q1.empty();
    }
};
相关推荐
一点媛艺1 小时前
Kotlin函数由易到难
开发语言·python·kotlin
姑苏风1 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
奋斗的小花生2 小时前
c++ 多态性
开发语言·c++
魔道不误砍柴功2 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
闲晨2 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程2 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
UestcXiye3 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
Chrikk3 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*3 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue3 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang