In C ++ deque, stack, queue and priority_queue

In C ++ deque, stack, queue and priority_queue

一, and

1.1. Deque Introduction

  1. the deque (pronounced like "deck"), is a deque irregular Acronym deque sequence is dynamically sized containers, which may be
    at both ends of the image expands and contracts.
  2. Deque specific library can be achieved in different ways, but usually a dynamic array. Whatever the case, which allows both
    to access a single element direct random access iterators, dynamically scalable according to need.
  3. Thus, the vector provides some deque similar functionality, but deque data insert and delete operations more high head and tail
    effect. The difference is that the vector, deque not guarantee that all the elements stored in contiguous space in the deque by adding an offset pointer
    access elements may cause the amount embodiment illegal operation.
  4. vector and list provides a similar interface, so it has a similar purpose, but the different implementations of the principles of the inside of: vector that use a
    dynamic array, which typically requires dynamic growth; the deque the elements may be dispersed in different memory blocks, stored in a deque of
    the necessary information, any element commonly used in the direct access range of a deque constant, so the internal deque implement than
    complex Vector, but extra information such dque growth in some cases a more efficient , particularly in large series, redistribution
    lower distribution cost is relatively high.
  5. In addition to frequent insertion and deletion operations in the outer head or tail, deque worse than the performance of the list and forward_list

1.2 deque schematic bottom

Here Insert Picture Description
1.3 deque application scenarios

deque在序列式容器中比较鸡肋,因为如果只是简单的存储元素,使用vector即可,如果对元素任意位置进行插入或者删除操作比较多,使用list即可,所以一般很少去使用deque。deque最大的应用,就是用其作为标准库中stack和queue的底层结构

二、stack

2.1 stack的介绍

  1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
  2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
  3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:
    empty:判空操作
    back:获取尾部元素操作
    push_back:尾部插入元素操作
    pop_back:尾部删除元素操作
  4. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,
    默认情况下使用deque。

2.2stack的模拟实现

namespace wolf
{
#include<deque>
template<class T, class Con = deque<T>>
class stack
{
public:
stack() {}
void push(const T& x) {_c.push_back(x);}
void pop() {_c.pop_back();}
T& top() {return _c.back();}
const T& top()const {return _c.back();}
size_t size()const {return _c.size();}
bool empty()const {return _c.empty();}
private:
Con _c;
};
}

三、queue

3.1 queue的介绍

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
  3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操
    作:
    empty:检测队列是否为空
    size:返回队列中有效元素的个数
    front:返回队头元素的引用
    back:返回队尾元素的引用
    push_back:在队列尾部入队列
    pop_front:在队列头部出队列
  4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque

3.2queue的模拟实现

#include<deque>
namespace bite
{
template<class T, class Con = deque<T>>
class queue
{
public:
queue() {}
void push(const T& x) {_c.push_back(x);}
void pop() {_c.pop_front();}
T& back() {return _c.back();}
const T& back()const {return _c.back();}
T& front() {return _c.front();}
const T& front()const {return _c.front();}
size_t size()const {return _c.size();}
bool empty()const {return _c.empty();}
private:
Con _c;
};
}

四、priority_queue

4.1 priority_queue的介绍

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元
    素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特
    定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭
    代器访问,并支持以下操作:
    empty():检测容器是否为空
    size():返回容器中有效元素个数
    front():返回容器中第一个元素的引用
    push_back():在容器尾部插入元素
    pop_back():删除容器尾部元素
  5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指
    定容器类,则使用vector。
  6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数
    make_heap、push_heap和pop_heap来自动完成此操作

4.2 priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆

处理内置类型的情况:

#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{
// 默认情况下,创建的是大堆,其底层按照小于号比较
vector<int> v{3,2,7,6,0,4,1,9,8,5};
priority_queue<int> q1;
for (auto& e : v)
q1.push(e);
cout << q1.top() << endl;
// 如果要创建小堆,将第三个模板参数换成greater比较方式
priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
cout << q2.top() << endl;
}

如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载

class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
bool operator<(const Date& d)const
{
return (_year < d._year) ||
(_year == d._year && _month < d._month) ||
(_year == d._year && _month == d._month && _day < d._day);
}
bool operator>(const Date& d)const
{
return (_year > d._year) ||
(_year == d._year && _month > d._month) ||
(_year == d._year && _month == d._month && _day > d._day);
}
friend ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
void TestPriorityQueue()
{
// 大堆,需要用户在自定义类型中提供<的重载
priority_queue<Date> q1;
q1.push(Date(2018, 10, 29));
q1.push(Date(2018, 10, 28));
q1.push(Date(2018, 10, 30));
cout << q1.top() << endl;
// 如果要创建小堆,需要用户提供>的重载
priority_queue<Date, vector<Date>, greater<Date>> q2;
q2.push(Date(2018, 10, 29));
q2.push(Date(2018, 10, 28));
q2.push(Date(2018, 10, 30));
cout << q2.top() << endl;
}

有些情况下,用户可能需要提供比较器规则

class Less
{
public:
bool operator()(const Date* pLeft, const Date* pRight)
{
return *pLeft < *pRight;
}
};
void TestPriorityQueue()
{
// 自己定制比较的规则
priority_queue<Date*, vector<Date*>, Less> q;
q.push(&Date(2018, 10, 29));
q.push(&Date(2018, 10, 28));
q.push(&Date(2018, 10, 30));
cout << *q.top() << endl;
return 0;
}

4.4 priority_queue的模拟实现

namespace wolf
{
template <class T, class Sequence = vector<T>, class Compare = less<T> >
class priority_queue
{
public:
priority_queue() : c()
{}
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last)
: c(first, last)
{
make_heap(c.begin(), c.end(), comp);
}
bool empty() const { return c.empty(); }
size_t size() const { return c.size(); }
T& top() const { return c.front(); }
void push(const T& x)
{
c.push_back(x);
push_heap(c.begin(), c.end(), comp);
}
void pop()
{
pop_heap(c.begin(), c.end(), comp);
c.pop_back();
}
private:
Sequence c;
Compare comp;
};
}

五、相关注意事项

1.为什么使用deque作为stack和queue的底层结构?

首先deque的优点是头部和尾部操作方便,效率可以达到O(1),缺点是因为deque的底层空间是不连续的,之所以看起来是连续的都是迭代器的功劳,正是因为deque的空间不连续,所以对其遍历的效率极低,因为要判断寻找下一段空间的位置。而stack和queue是作为容器适配器来使用的,其不需要遍历,只需要在两端进行操作即可,刚好避开deque的缺陷。虽然其他容器也可以作为stack和queue的底层结构,但效率都没有deque高

2.什么是容器适配器?

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

3. Why the stack, queue and priority_queue referred to as container adapter?

Although the stack, queue, priority_queue can also be stored in the elements, but in the STL and not be divided in the ranks of the vessel, but it is called a container adapter, because at the bottom of each container has its own implementation, the stack, queue, priority_queue only in the other container bottom is encapsulated

Published 66 original articles · won praise 28 · views 60000 +

Guess you like

Origin blog.csdn.net/wolfGuiDao/article/details/104251246