【c++】stack和queue模拟实现

简介: 【c++】stack和queue模拟实现

> 作者简介:დ旧言~,目前大二,现在学习Java,c,c++,Python等

> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:能手撕stack和queue模拟

> 毒鸡汤:过错是暂时的遗憾,而错过则是永远的遗憾!

> 望小伙伴们点赞👍收藏✨加关注哟💕💕  



🌟前言

       手撕stack和queue对比在数据结构中的模拟要比较简单,为什么呢?因为我们学习了参数模板这块,我们可以调用,所以模拟起来比较简单,具体是如何简单法呢,我们进入正文:



⭐主体

这里我们创建三个文件:stack.h,queue.h,test.cpp。



第一个:迭代器模式

迭代器模式就是在不暴露底层的细节的前提下,通过封装给用户提供统一的接口让用户访问容器里面的数据,我们使用的每个容器都可以通过创建迭代器变量的方式来访问容器里面的内容,并且访问的方式都是一样的,(*迭代器变量)可以得到并修改指定位置的数据,(迭代器++)可以让迭代器变量指向容器的下一个元素,通过上面的两个操作,不管是vector容器还是string容器还是后面要学的更加复杂的容器,我们都可以很简单的访问容器里面的内容,但是这些迭代器底层实现的原理是一样的吗?vector和string迭代器是通过创建一个指针变量来实现的,而list迭代器是创建一个类,通过这个类对list的数据进行封装来实现的,不同的容器的迭代器实现的方法也各不相同,但是作为使用者来说我们根本就不用了解这些迭代器的底层实现我们会用就行,并且迭代器的出现很大程度上降低了我们学习的成本,并且迭代器的出现还有助于维护数据的安全,如果我们认为的操作容器里面的数据的话,搞不好就将哪个重要的数据删除了,将另外一个地方的数据覆盖了,所以迭代器模式就对容器里面的数据进行了一下封装,我们要访问这些数据就只能通过迭代器的方式来进行访问,这样即降低了学习成本又保护了数据的安全,我们把这样的设计模式成为迭代器模式。


第二个:适配器模式

在之前的学习中我们知道stack对数据管理的方式是先入栈的数据后出栈,后入栈的数据先出栈,我们还知道queue对数据管理的方式是:先入队列的数据先出队列,后入队列的数据后出队列,这是两个容器对数据处理的方式,虽然这种处理数据的方式属于这些容器的,但是其他的容器也可以实现这样的功能,比如说vector和list都可以在容器的头部或者尾部插入或删除数据,如果我们只让vector或者list在容器的头部尾部插入删除数据的话,那是不是就相当于是stack了呢?如果我们只让vector或者list在头部删除数据在尾部插入数据的话,那这是不是就相当于queue了呢?所以在实现一个容器或者功能的时候,我们可以用现有东西进行一些简单的修改或者封装从而实现你想要的东西,那么这就是适配器模式:用已有的东西通过封装转换出来你想要的东西,那么我们这里的stack和queue就可以通过适配器模式来实现。


🌙stack模拟实现

      模拟实现C++的栈时,应该要考虑用什么作为它的底层,目前来看,貌似动态数组vector是个不错的选择,因为栈只需要在栈顶插入和删除元素。


     第二个模板参数的默认值给成vector<T>,这样就不需要用户自己传递第二个参数了。实现stack中的函数也很容易,只需要在函数内部调用vector的函数即可。

框架如下:

#include<iostream>
#include<vector>
#include<list>
 
using namespace std;
namespace lyk
{
  template<class T, class continer = vector<int>>
  class stack
  {
  public:
  private:
    continer con;
  };
}

💫元素入栈

首先来实现一下stack的push函数因为stack在插入数据的时候只能在尾部插入数据,所以在stack的push函数里面就可以直接调用容器con的push_back函数来尾插数据,那么该函数的实现如下:

// 元素入栈
void push(const T& val)
{
  con.push_back(val);
}


💫元素出栈

stack中删除数据也只能删除尾部数组,所以实现pop函数的时候就可以直接调用容器con的pop_back函数来删除数据,那这里的代码就如下:

// 元素出栈
void pop()
{
  con.pop_back();
}


💫获取元素有效个数

复用vector类的函数即可获得有效元素的个数,代码如下:

// 返回栈中元素个数
size_t size()
{
  return con.size();
}


💫判断栈是否为空

复用vector类的判空函数,代码如下:

// 判断栈是否为空
bool empty()
{
  return con.empty();
}


💫返回栈顶元素

代码如下:

// 返回栈顶元素
const T& top()
{
  return con.back();
}


💫stack整体代码

#include<iostream>
#include<vector>
#include<list>
 
using namespace std;
namespace lyk
{
  template<class T, class continer = vector<int>>
  class stack
  {
  public:
    // 元素入栈
    void push(const T& val)
    {
      con.push_back(val);
    }
    // 元素出栈
    void pop()
    {
      con.pop_back();
    }
    // 返回栈中元素个数
    size_t size()
    {
      return con.size();
    }
    // 判断栈是否为空
    bool empty()
    {
      return con.empty();
    }
    // 返回栈顶元素
    const T& top()
    {
      return con.back();
    }
  private:
    continer con;
  };
  // 测试
  void test_stack()
  {
    lyk::stack<int> s1;
    s1.push(1);
    s1.push(2);
    s1.push(3);
    s1.push(4);
    while (!s1.empty())
    {
      cout << s1.top() << " ";
      s1.pop();
    }
    cout << endl;
    lyk::stack<int, list<int>> s2;
    s2.push(4);
    s2.push(3);
    s2.push(2);
    s2.push(1);
    while (!s2.empty())
    {
      cout << s2.top() << " ";
      s2.pop();
    }
  }
}


运行结果:



🌙queue模拟实现

同样的道理queue也要容纳各种数据,也可以由各种容器作为底层来容纳数据,所以queue也得创建一个模板,并且模板里面也得有两个参数,因为queue是在容器的头部删除数据,在容器的尾部插入数据,所以给第二个参数的缺省值最好是deque,那么这里的代码就如下:

#include<iostream>
#include<vector>
#include<list>
#include<deque>
 
namespace lyk
{
  template<class T, class continer = deque<T>>
  class queue
  {
  public:
  private:
    continer con;
  };
 
}


💫元素入队列

因为queue插入数据是在容器的尾部插入数据,所以在实现queue的push函数时可以通过调用con的push_back函数来实现,那这里的代码如下:

// 元素入队列
void push(const T& val)
{
  con.push_back(val);
}


💫元素出队列

queue的pop函数是在容器的头部删除数据所以这里可以调用容器的pop_front函数来实现,那么这里的代码如下:

// 元素出队列
void pop()
{
  con.pop_front();
}


💫返回队列元素个数

调用内部容器函数来进行实现,那么这里的代码就如下:

// 返回队列元素个数
size_t size()
{
  return con.size();
}


💫判断队列是否为空

调用内部容器函数来进行实现,那么这里的代码就如下:

// 判断队列是否为空
bool empty()
{
  return con.empty();
}


💫返回队头元素的引用

调用内部容器函数来进行实现,那么这里的代码就如下:

// 返回队头元素的引用
const T& front()
{
  return con.front();
}


💫返回队头元素的引用

调用内部容器函数来进行实现,那么这里的代码就如下:

// 返回队头元素的引用
const T& back()
{
    return con.back();
}


💫queue整体代码

#include<iostream>
#include<vector>
#include<list>
#include<deque>
 
namespace lyk
{
  template<class T, class continer = deque<T>>
  class queue
  {
  public:
    // 元素入队列
    void push(const T& val)
    {
      con.push_back(val);
    }
    // 元素出队列
    void pop()
    {
      con.pop_front();
    }
    // 返回队列元素个数
    size_t size()
    {
      return con.size();
    }
    // 判断队列是否为空
    bool empty()
    {
      return con.empty();
    }
    // 返回队头元素的引用
    const T& front()
    {
      return con.front();
    }
    // 返回队头元素的引用
    const T& back()
    {
      return con.back();
    }
  private:
    continer con;
  };
 
  void test_queue()
  {
    //bit::queue<int> q;
    // 
    // vector不能适配
    //bit::queue<int, vector<int>> q;
    lyk::queue<int, list<int>> q;
 
    q.push(1);
    q.push(2);
    q.push(3);
    q.push(4);
 
    while (!q.empty())
    {
      cout << q.front() << " ";
      q.pop();
    }
    cout << endl;
  }
 
}


运行结果:



🌟结束语

      今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。


目录
相关文章
|
2天前
|
C++ 容器
【c++丨STL】stack和queue的使用及模拟实现
本文介绍了STL中的两个重要容器适配器:栈(stack)和队列(queue)。容器适配器是在已有容器基础上添加新特性或功能的结构,如栈基于顺序表或链表限制操作实现。文章详细讲解了stack和queue的主要成员函数(empty、size、top/front/back、push/pop、swap),并提供了使用示例和模拟实现代码。通过这些内容,读者可以更好地理解这两种数据结构的工作原理及其实现方法。最后,作者鼓励读者点赞支持。 总结:本文深入浅出地讲解了STL中stack和queue的使用方法及其模拟实现,帮助读者掌握这两种容器适配器的特性和应用场景。
35 21
|
3月前
|
存储 算法 调度
【C++打怪之路Lv11】-- stack、queue和优先级队列
【C++打怪之路Lv11】-- stack、queue和优先级队列
53 1
|
3月前
|
设计模式 存储 C++
C++之stack 和 queue(下)
C++之stack 和 queue(下)
51 1
|
3月前
|
C++ 容器
C++之stack 和 queue(上)
C++之stack 和 queue(上)
92 0
|
3月前
|
存储 C++ 容器
C++番外篇——stack、queue的实现及deque的介绍
C++番外篇——stack、queue的实现及deque的介绍
37 0
|
3月前
|
存储 算法 C++
C++入门10——stack与queue的使用
C++入门10——stack与queue的使用
57 0
|
3月前
|
C++
【C++】C++ STL探索:Priority Queue与仿函数的深入解析(三)
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
|
17天前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
58 19
|
17天前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
40 13
|
17天前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
43 5