【C++】STL中的容器适配器 stack queue 和 priority_queue 的模拟实现

简介: 【C++】STL中的容器适配器 stack queue 和 priority_queue 的模拟实现

一、容器适配器

1、什么是容器适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

例如我们常见的充电器就是一种适配器,它将我们常用的220V交流电压转化为4,5V (或者其他更高的电压) 的直流电压来给我们的电子设备进行充电。

2、STL标准库中的容器适配器

虽然stackqueuepriority_queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配

器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stackqueue默认使用dequepriority_queue默认使用了vector

二、stack的模拟实现

1、stack的简单介绍

相关文档

stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,但这些容器类应该支持以下操作:

  • empty:判空操作
  • back:获取尾部元素操作
  • push_back:尾部插入元素操作
  • pop_back:尾部删除元素操作

标准容器vectordequelist均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque

2、栈的模拟实现

为了栈的通用性,这里我们使用模板来进行模拟栈,关于栈的底层容器这里我们选择vector来进行模拟实现。

template<class T, class Container = vector<T>>
class stack
{
public:
  //栈的插入
  void push(const T& val)
  {
    //使用底层容器中尾插函数进行插入,栈只能进行栈顶插入和删除
    _con.push_back(val);
  }
  //栈的删除
  void pop()
  {
    //使用底层容器中尾删函数进行插入,栈只能进行栈顶插入和删除
    _con.pop_back();
  }
  //获取栈顶元素
  T& top()
  {
    //返回底层容器中最后一个数据
    return _con.back();
  }
  //const 版本
  const T& top() const
  {
    return _con.back();
  }
  //获取数据个数
  size_t size() const
  {
    //返回底层容器中数据的个数
    return _con.size();
  }
  //判空函数
  bool empty() const
  {
    //对底层容器进行判空
    return _con.empty();
  }
private:
  //成员变量是一个容器创建的对象
  Container _con;
};

三、queue的模拟实现

1、queue的简单介绍

相关文档

queue底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:

  • empty:检测队列是否为空
  • size:返回队列中有效元素的个数
  • front:返回队头元素的引用
  • back:返回队尾元素的引用
  • push_back:在队列尾部入队列
  • pop_front:在队列头部出队列

标准容器类dequelist满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque

2、queue的模拟实现

为了queue的通用性,这里我们使用模板来进行模拟queue,关于queue的底层容器这里我们选择list来进行模拟实现。

template<class T, class Container = list<T>>
class queue
{
public:
  //队列的插入
  void push(const T& val)
  {
    //使用底层容器中尾插函数进行插入,队列只能进行队尾插入
    _com.push_back(val);
  }
  //队列的删除
  void pop()
  {
    //使用底层容器中头删函数进行删除,队列只能进行队头删除
    _com.pop_front();
  }
  //获取队头数据
  T& front()
  {
    //返回底层容器中第一个数据
    return _com.front();
  }
  //const版本
  const T& front() const
  {
    return _com.front();
  }
  //获取队尾数据
  T& back()
  {
    //返回底层容器中最后一个数据
    return _com.back();
  }
  //const版本
  const T& back() const
  {
    return _com.back();
  }
  //获取数据个数
  size_t size() const
  {
    //返回底层容器中数据的个数
    return _com.size();
  }
  //判空函数
  bool empty() const
  {
    //对底层容器进行判空
    return _com.empty();
  }
private:
  //成员变量是一个容器创建的对象
  Container _com;
};

四、priority_queue的模拟实现

1、priority_queue的简单介绍

相关文档

优先队列是一种容器适配器,它其实就是我们数据结构中的,默认情况下priority_queue是大堆。

priority_queue底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:

  • empty:检测容器是否为空
  • size:返回容器中有效元素个数
  • front:返回容器中第一个元素的引用
  • push_back:在容器尾部插入元素
  • pop_back:删除容器尾部元素

标准容器类vectordeque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector

2、priority_queue的模拟实现

与前面的栈与队列一样priority_queue前两个模板参数都类似,但是priority_queue要有第三个模板参数,这个参数表示按照什么方式进行比较,即建立大堆还是小堆。

//第三个参数是比较方式,需要传递一个仿函数来确定比较方式,默认传递的是less<T>,表示建大堆
template<class T, class Container = vector<T>, class Comper = less<T>>
class priority_queue
{
public:
  //获取堆顶数据
  const T& top() const
  {
    //返回底层容器的第一个数据
    return _con.front();
  }
  //获取数据个数
  size_t size() const
  {
    //返回底层容器的数据个数
    return _con.size();
  }
  //判空函数
  bool empty() const
  {
    //判断底层容器是否为空
    return _con.empty();
  }
  //堆的插入
  void push(const T& val)
  {
    //从尾部插入
    _con.push_back(val);
    int child = _con.size() - 1;
    //向上调整重新建堆
    AdjustUp(child);
  }
  //堆的删除
  void pop()
  {
    //检查堆是否为空
    assert(!_con.empty());
    //交换堆顶与最后一个数据
    swap(_con.front(), _con.back());
    //删除最后一个数据
    _con.pop_back();
    //从堆顶进行向下调整,重新建堆
    AdjustDown(0);
  }
private:
  //向上调整算法
  void AdjustUp(int child)
  {
    Comper com;
    int parent = (child - 1) / 2;
    while (child > 0)
    {
      //这里使用了仿函数来判断建立什么堆
      //比较的位置(_con[parent], _con[child])是不能换的!!!
      if (com(_con[parent], _con[child]))
      {
        swap(_con[parent], _con[child]);
        child = parent;
        parent = (child - 1) / 2;
      }
      else
      {
        break;
      }
    }
  }
  //向下调整算法
  void AdjustDown(int parent)
  {
    Comper com;
    //默认左孩子更符合建堆的要求
    int child = parent *2 + 1;
    while (child < _con.size())
    {
      if (child + 1 < _con.size() && Comper()(_con[child], _con[child + 1]))
      {
        ++child;
      }
      if (com(_con[parent], _con[child]))
      {
        swap(_con[parent], _con[child]);
        parent = child;
        child = parent * 2 + 1;
      }
      else
      {
        break;
      }
    }
  }
  //成员变量是一个容器创建的对象
  Container _con;
};
相关文章
|
6月前
|
存储 设计模式 C++
【C++】优先级队列(容器适配器)
本文介绍了C++ STL中的线性容器及其适配器,包括栈、队列和优先队列的设计与实现。详细解析了`deque`的特点和存储结构,以及如何利用`deque`实现栈、队列和优先队列。通过自定义命名空间和类模板,展示了如何模拟实现这些容器适配器,重点讲解了优先队列的内部机制,如堆的构建与维护方法。
97 0
|
7月前
|
设计模式 存储 C++
【C++】C++ STL探索:容器适配器 Stack 与 Queue 的使用及模拟实现(二)
【C++】C++ STL探索:容器适配器 Stack 与 Queue 的使用及模拟实现
|
7月前
|
存储 C++ 容器
【C++】C++ STL探索:容器适配器 Stack 与 Queue 的使用及模拟实现(一)
【C++】C++ STL探索:容器适配器 Stack 与 Queue 的使用及模拟实现
|
10月前
|
存储 算法 C语言
【C++】详解STL的适配器容器之一:优先级队列 priority_queue
【C++】详解STL的适配器容器之一:优先级队列 priority_queue
|
3月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
28天前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
54 12
|
2月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
58 16
|
2月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
2月前
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
3月前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)

热门文章

最新文章