- //构造函数
- stack<T> stkT;//stack采用模板类实现,stack对象的默认构造形式
- stack(const stack &stk);//拷贝构造函数
- //赋值操作
- stack&operator=(const stack &stk)//重载等号操作符
- //数据存取操作
- push(elem);//向栈顶添加元素
- pop();//从栈顶移除一个元素
- top();//返回栈顶元素
- //容量大小操作
- empty();//判断堆栈是否为空
- size();//返回堆栈的大小
queue是一种先进后出的数据结构(队列),它有两个出口,queue容器允许从一端新增元素,另一端移除元素

总结:
- //构造函数
- queue<T> queT;//queue采用模板类实现,queue对象的默认构造函数
- queue(const queue &que);//拷贝构造函数
- //存取、插入和删除操作
- push(elem);//往队尾添加元素
- pop();//从对头移除第一个元素
- back();//返回最后一个元素
- front();//返回第一个元素
- //赋值操作
- queue&operator=(const queue &que);//重载等号操作符
- //容量大小操作
- empty();//判断队列是否为空
- size();//返回队列的大小
适配器: 一种设计模式,该种模式是将一个类的接口转换成客户希望的另外一个接口。

可以看出的是,这两个容器相比我们之间见过的容器多了一个模板参数,也就是容器类的模板参数,他们在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,它们的底层是其他容器,对其他容器的接口进行了包装,它们的默认是使用deque(双端队列)
vector容器时单向开口的连续内存空间,deque则是一种双向开口的连续线性空间。双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
deque底层结构
它并不是一段连续的空间,而是由多个连续的小空间拼接而成,相当于一个动态的二维数组。
Deque容器是连续的空间,至少逻辑上看来如此,连续现行空间总是令我们联想到 array和vector,array无法成长,vector虽可成长,却只能向尾端成长,而且其成长其实 是一个假象,事实上(1)申请更大空间(2)原数据复制新空间(3)释放原空间三步骤,如果不是vector每次配置新的空间时都留有余裕,其成长假象所带来的代价是非常昂贵的。Deque是由一段一段的定量的连续空间构成。一旦有必要在前端或者尾端增加新的空间,便配置一段连续定量的空间,串接在deque的头端或者尾端。Deque最大的工作就是维护这些分段连续的内存空间的整体性的假象并提供随机存取的接口,避开了重新配置空间,复制,释放的轮回,代价就是复杂的迭代器架构。 既然deque是分段连续内存空间,那么就必须有中央控制,维持整体连续的假象数据结构的设计及迭代器的前进后退操作颇为繁琐。Deque代码的实现远比vector或list都多得多。

Deque采取一块所谓的map作为主控,这里所谓的map是一小块连续的内存空间,其中每一个元素(此时成为一个结点)都是一个指针,指向另一段连续的内存空间,称作缓冲区,缓冲区才是deque的存储空间的主体。
deque的迭代器:

deque的优点:
deque的缺点:
deque可以作为stack和queue底层默认容器的原因:
- template<class T, class Container = deque
> - class stack
- {
- public:
- void push(const T& x)
- {
- _con.push_back(x);
- }
- void pop()
- {
- _con.pop_back();
- }
- T top()
- {
- return _con.back();
- }
- size_t size()
- {
- return _con.size();
- }
- bool empty()
- {
- return _con.empty();
- }
- private:
- Container _con;
- };
-
-
- template<class T, class Container = deque
> - class queue
- {
- public:
- void push(const T& x)
- {
- _con.push_back(x);
- }
- void pop()
- {
- _con.pop_front();
- }
- T& front()
- {
- return _con.front();
- }
- T& back()
- {
- return _con.back();
- }
- size_t size()
- {
- return _con.size();
- }
- bool empty()
- {
- return _con.empty();
- }
- private:
- Container _con;
- };
- template <typename T, typename Container=std::vector<T>, typename Compare=std::less<T>>
- class priority_queue
priority_queue 实例默认有一个 vector 容器。函数对象类型 less 是一个默认的排序断言,定义在头文件 function 中,决定了容器中最大的元素会排在队列前面。fonction 中定义了 greater,用来作为模板的最后一个参数对元素排序,最小元素会排在队列前面。当然,如果指定模板的最后一个参数,就必须提供另外的两个模板类型参数。
总结几点:
- push(const T& obj);//将obj的副本放到容器的适当位置,这通常会包含一个排序操作。
- push(T&& obj);//将obj放到容器的适当位置,这通常会包含一个排序操作。
- emplace(T constructor a rgs...);//通过调用传入参数的构造函数,在序列的适当位置构造一个T对象。为了维持优先顺序,通常需要一个排序操作。
- top();//返回优先级队列中第一个元素的引用。
- pop();//移除第一个元素。
- size();//返回队列中元素的个数。
- empty();//如果队列为空的话,返回true。
- swap(priority_queue<T>& other);//和参数的元素进行交换,所包含对象的类型必须相同。
- void test_priority_queue()
- {
- priority_queue<int, vector<int>> pq;
- pq.push(5);
- pq.push(7);
- pq.push(4);
- pq.push(2);
- pq.push(6);
-
- while (!pq.empty())
- {
- cout << pq.top() << " ";
- pq.pop();
- }
- cout << endl;
- }
其中模板中有三个参数,最后一个参数是仿函数,也就是指明优先级队列是按照升序还是降序来存数据的
- template<class T, class Container = vector<T>, class Compare = less<T>>// 默认是小于
- class priority_queue
- {
- public:
- private:
- Container _con;
- Compare _com;
- };
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
- // 仿函数 就是一个类重载了一个(),operator(),可以像函数一样使用
- template<class T>
- struct greater
- {
- bool operator()(const T& a, const T& b)
- {
- return a > b;
- }
- };
- template<class T>
- struct less
- {
- bool operator()(const T& a, const T& b)
- {
- return a < b;
- }
- };
可以看出,仿函数就是用一个类封装一个成员函数operator(),使得这个类的对象可以像函数一样去调用。
实例演示:
- template<class T>
- struct IsEqual
- {
- bool operator()(const T& a, const T& b)
- {
- return a == b;
- }
- };
- void test()
- {
- IsEqual<int> ie;
- cout << ie(2, 3) << endl;// 该类实例化出的对象可以具有函数行为
- }
向上调整: 从最后一个数往上调整
- void AdjustUp(int child)
- {
- int parent = (child - 1) / 2;
- while (child > 0)
- {
- if (_con[child] > _con[parent])//< 建小堆 > 建大堆
- {
- swap(_con[child], _con[parent]);
- child = parent;
- parent = (child - 1) / 2;
- }
- else
- {
- break;
- }
- }
- }
向下调整: 从第一个往下调整
- void AdjustDown(int parent)
- {
- int child = parent * 2 + 1;
- while (child < (int)size())
- {
- if (child + 1 < (int)size() && _con[child + 1] > _con[child])
- {
- ++child;
- }
- if (_con[child] > _con[parent])// 建小堆
- {
- swap(_con[child], _con[parent]);
- parent = child;
- child = parent * 2 + 1;
- }
- else
- {
- break;
- }
- }
- }
这两个函数用仿函数实现后如下:
- void AdjustUp(int child)
- {
- int parent = (child - 1) / 2;
- while (child > 0)
- {
- if (_com(_con[parent], _con[child]))// _con[child] > _con[parent]
- {
- swap(_con[child], _con[parent]);
- child = parent;
- parent = (child - 1) / 2;
- }
- else
- {
- break;
- }
- }
- }
- void AdjustDown(int parent)
- {
- int child = parent * 2 + 1;
- while (child < (int)size())
- {
- if (child + 1 < (int)size() && _com(_con[child], _con[child + 1]))// _con[child + 1] > _con[child]
- {
- ++child;
- }
- if (_com(_con[parent], _con[child]))// _con[child] > _con[parent]
- {
- swap(_con[child], _con[parent]);
- parent = child;
- child = parent * 2 + 1;
- }
- else
- {
- break;
- }
- }
- }
push 先在队尾插入数据,然后用向上调整算法使得堆是大堆或小堆
- void push(const T& x)
- {
- _con.push_back(x);
- AdjustUp((int)size() - 1);
- }
-
pop 先将堆顶的元素和队尾的元素交换,再删去队尾元素(而不是直接删去堆顶元素,这样会破坏堆的结构,然后又要建堆),然后再使用向下调整算法使得堆是大堆或小堆
- void pop()
- {
- assert(!empty());
- swap(_con[0], _con[(int)size() - 1]);
- _con.pop_back();
- AdjustDown(0);
- }
//top 返回堆顶元素 T& top() { assert(!empty()); return _con[0]; } //size 返回优先级队列元素个数 size_t size() { return _con.size(); } //empty 判断优先级队列是否为空 bool empty() { return size() == 0;