C++:deque的概念以及stack和queue的模拟实现

简介: C++:deque的概念以及stack和queue的模拟实现

本篇主要总结的是stackqueue的模拟实现以及deque的原理

stack的模拟实现

和前面的模拟实现相同,首先要看官方实现的功能

这里引入了Container的概念,从字面意思来看,也就是说,在实例化模板的时候实际上是需要实例化两个参数的,一个是栈内元素的数据类型,一个是容器的类型,这里通过缺省参数给定了一个deque,因此平时使用的时候不需要实例化第二个参数,关于deque的概念后面再进行讲解

从中可以看出,stack的实现是以一个容器为模板,在这个模板的基础上引申出了栈的概念,因此在模拟实现的过程中相对容易一些

#include <iostream>
#include <vector>
#include <list>
#include <deque>
namespace mystack
{
  template <class T,class Container>
  class stack
  {
  public:
    void push(const T& x)
    {
      _con.push_back(x);
    }
    void pop()
    {
      _con.pop_back();
    }
    bool empty()
    {
      return _con.empty();
    }
    int size()
    {
      return _con.size();
    }
    const T& top()
    {
      return _con.back();
    }
  private:
    Container _con;
  };
}

但是在实际的栈的实现中,是使用带有缺省参数的模板参数,在容器的实现中是使用的是deque,因此在使用STL中的模板实例化只需要实例化第一个参数即可,默认是使用的是deque,也可以实例化为vectorlist

#include "stack.h"
#include <stack>
// 采用vector来当容器生成栈
int main()
{
  std::stack<int, std::vector<int>> s;
  // 入栈
  s.push(1);
  s.push(2);
  s.push(3);
  s.push(4);
  // 出栈
  while (!s.empty())
  {
    std::cout << s.top() << " ";
    s.pop();
  }
  return 0;
}
#include "stack.h"
#include <stack>
// 采用list来当容器生成栈
int main()
{
  std::stack<int, std::list<int>> s;
  // 入栈
  s.push(1);
  s.push(2);
  s.push(3);
  s.push(4);
  // 出栈
  while (!s.empty())
  {
    std::cout << s.top() << " ";
    s.pop();
  }
  return 0;
}

deque

那么下面引入deque的概念,什么是deque,它的用法又是什么?

还是从cplusplus中查阅它的概念

deque的概念

deque也被叫做双端队列,从名字可以看出通俗来说它就是在两端都可以进出的队列,因此可以随意的头插头删尾插尾删

deque的底层实现方式

从上面的内容可以知道,deque可以实现在容器两端进行插入删除的操作,那其内部是如何进行工作的?

上面是两个容器的不同点,其实可以看出vector的优点其实就对应的是list的缺点,而list的优点就对应了vector的缺点,因此才需要根据不同的实验需求选择对应的容器来使用

deque的原理

deque的原理就是采用了一个中控数组用来管理每一个小数组,所以中控数组是一个指针数组,其中存储的是每一个小数组的指针,当需要插入元素的时候,就在这个中控数组的中间部分的指针指向的元素中进行插入,这样的模式就导致它具备了向前面插入数据,也具备了向后面插入数据的能力

向头部和尾部插入数据:


如果头部有数据继续头插,那么就会开辟额外的小数组用来存储

但是这样就产生了一个问题,如果中控数组控制的小数组已经排满了数据,但是我还要实施插入的操作应该如何实现?

  1. 可以整体挪动数据,把所有数组向后挪动一位
  2. 可以对单个小数组进行扩容,使得每一个小数组的长度不一样

这是两种扩容的思路,如果采用第一种思路,那么在挪动数据的时候效率会很低,但是优势是进行下标的随机访问的时候拥有更高效的功能

如果采用的是第二种思路,那么在插入数据的时候成本很低,但是在访问的时候就有较高的计算成本

因此两种扩容思路都有其好的一点和坏的一点,具体如何实施看对于容器的需求如何

在实际的使用场景中,对于deque其实是不常用的,因为它兼容了vectorlist的部分优势,但是在随机访问的情况下不如vector,在插入删除数据的方面不如list,只是出于一个较为兼容的容器,在实际开发中应用场景并不多

那为什么会选择deque作为底层容器的实现?

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()pop_back()操作的线性结构,都可以作为stack的底层容器,比如vectorlist都可以; queue是先进先出的特殊线性数据结构,只要具有push_back()pop_front()操作的线性结构,都可以作为queue的底层容器,比如list

但是STL中对stackqueue默认选择deque作为其底层容器,主要是因为:

  1. stackqueue不需要遍历(因此stackqueue没有迭代器),只需要在固定的一端或者两端进行操作
  2. stack中元素增长时,dequevector的效率高(扩容时不需要搬移大量数据); queue中的元素增长时,deque不仅效率高,而且内存使用率高,结合了deque的优点,而完美的避开了其缺陷

queue的模拟实现

#include <iostream>
#include <vector>
#include <list>
#include <deque>
namespace myqueue
{
  template <class T,class Container=std::deque<T>>
  class queue
  {
  public:
    void push(const T& x)
    {
      _con.push_back(x);
    }
    void pop()
    {
      _con.pop_front();
    }
    bool empty()
    {
      return _con.empty();
    }
    int size()
    {
      return _con.size();
    }
    const T& front()
    {
      return _con.front();
    }
    const T& back()
    {
      return _con.back();
    }
  private:
    Container _con;
  };
}
#include "queue.h"
int main()
{
  myqueue::queue<int> q;
  // 插入数据
  q.push(1);
  q.push(2);
  q.push(3);
  q.push(4);
  q.push(5);
  // 打印数据
  while (!q.empty())
  {
    std::cout << q.front() << " ";
    q.pop();
  }
  return 0;
}

这里直接使用了deque作为缺省参数,因此实例化只需要使用一个参数即可

相关文章
|
11月前
|
存储 编译器 C++
【c++】多态(多态的概念及实现、虚函数重写、纯虚函数和抽象类、虚函数表、多态的实现过程)
本文介绍了面向对象编程中的多态特性,涵盖其概念、实现条件及原理。多态指“一个接口,多种实现”,通过基类指针或引用来调用不同派生类的重写虚函数,实现运行时多态。文中详细解释了虚函数、虚函数表(vtable)、纯虚函数与抽象类的概念,并通过代码示例展示了多态的具体应用。此外,还讨论了动态绑定和静态绑定的区别,帮助读者深入理解多态机制。最后总结了多态在编程中的重要性和应用场景。 文章结构清晰,从基础到深入,适合初学者和有一定基础的开发者学习。如果你觉得内容有帮助,请点赞支持。 ❤❤❤
1354 0
|
设计模式 C++ 容器
c++中的Stack与Queue
c++中的Stack与Queue
|
C++ 容器
【c++丨STL】stack和queue的使用及模拟实现
本文介绍了STL中的两个重要容器适配器:栈(stack)和队列(queue)。容器适配器是在已有容器基础上添加新特性或功能的结构,如栈基于顺序表或链表限制操作实现。文章详细讲解了stack和queue的主要成员函数(empty、size、top/front/back、push/pop、swap),并提供了使用示例和模拟实现代码。通过这些内容,读者可以更好地理解这两种数据结构的工作原理及其实现方法。最后,作者鼓励读者点赞支持。 总结:本文深入浅出地讲解了STL中stack和queue的使用方法及其模拟实现,帮助读者掌握这两种容器适配器的特性和应用场景。
352 21
|
存储 算法 调度
【C++打怪之路Lv11】-- stack、queue和优先级队列
【C++打怪之路Lv11】-- stack、queue和优先级队列
221 1
|
设计模式 存储 C++
C++之stack 和 queue(下)
C++之stack 和 queue(下)
|
C++ 容器
C++之stack 和 queue(上)
C++之stack 和 queue(上)
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
10月前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
423 12
|
8月前
|
人工智能 机器人 编译器
c++模板初阶----函数模板与类模板
class 类模板名private://类内成员声明class Apublic:A(T val):a(val){}private:T a;return 0;运行结果:注意:类模板中的成员函数若是放在类外定义时,需要加模板参数列表。return 0;
227 0
|
8月前
|
存储 编译器 程序员
c++的类(附含explicit关键字,友元,内部类)
本文介绍了C++中类的核心概念与用法,涵盖封装、继承、多态三大特性。重点讲解了类的定义(`class`与`struct`)、访问限定符(`private`、`public`、`protected`)、类的作用域及成员函数的声明与定义分离。同时深入探讨了类的大小计算、`this`指针、默认成员函数(构造函数、析构函数、拷贝构造、赋值重载)以及运算符重载等内容。 文章还详细分析了`explicit`关键字的作用、静态成员(变量与函数)、友元(友元函数与友元类)的概念及其使用场景,并简要介绍了内部类的特性。
361 0