namespace czh
{
template<class T, class Container = deque<T>>
// template>
// template>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
T& top()
{
return _con.back();
}
const T& top() const
{
return _con.back();
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
原理
1、deque(双端队列):是一种双开口的"连续"空间的数据结构,注意和 queue 没有关系双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
2、deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组
3、双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
为何作为stack 和 queue的 默认 实现
但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:
所以结合了deque的优点,完美递避开了其缺陷
因为queue的接口中存在头删和尾插,因此使用vector来封装效率太低,故可以借助list 和 deque 来模拟实现queue
namespace czh
{
// 设计模式 -- 适配器模式(配接器)
template<class T, class Container = deque<T>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
T& front()
{
return _con.front();
}
const T& front() const
{
return _con.front();
}
T& back()
{
return _con.back();
}
const T& back() const
{
return _con.back();
}
size_t size() const
{
return _con.size();
}
bool empty() const
{
return _con.empty();
}
private:
Container _con;
};
}
优先级队列默认使用 vector 作为其底层存储数据的容器,在 vector 上又使用了堆算法将 vector 中元素构成堆的结构,因此 priority_queue 就是堆,所以所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认priority_queue 是大堆。通过仿函数可以改变其为小堆。
#include
#include
#include
#include // 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;
}
仿函数准确来说是一个类,这个类重载了operator(),这个类的对象调用 operator(),可以像函数一样去使用,在优先级队列中的使用可以控制创建的优先级队列中是大堆还是小堆使其可以像水龙头的开关一样可以去控制热水还是凉水。在 STL库中中的两个仿函数的实现如下:
比如我们想买个手机,在京东上搜索手机,可以按照价格、销量等标签排序,那么我们可以利用仿函数简单实现,写一个商品类代表手机我们用排序算法 sort 对其排序,但是我们不可以在类的内部重载 > < 运算符,因为我们并不知道如何排序,按照什么标准排序,为了更好说明问题,来盘代码。
#include
#include "priority_queue.h"
#include
#include
using namespace std;
//仿函数的应用
struct Phone{
int saleNum;
int price;
//.....
};
struct LessPhonePrice{
bool operator()(const Phone& p1, const Phone& p2)
{
return p1.price < p2.price;
}
};
struct LessPhoneSaleNum{
bool operator()(const Phone& p1, const Phone& p2)
{
return p1.saleNum < p2.saleNum;
}
};
void TestSort()
{
vector <Phone> gv = {
{
1, 3 }, {
5, 2 }, {
2, 10 } };
sort(gv.begin(), gv.end(),LessPhoneSaleNum());//匿名对象会在STL中调用我自己写的比较方法
sort(gv.begin(), gv.end(), LessPhonePrice());
}
int main()
{
TestSort();
return 0;
}
因为优先级队列的底层结构就是堆所以对 vector 进行适当封装就可以了,如果不知道堆的知识请参考我的另外一篇文章 https://blog.csdn.net/CZHLNN/article/details/112481962
#pragma once
//仿函数
template<class T>
class Less{
public:
bool operator()(const T& t1, const T& t2) const
{
return t1 < t2;
}
};
template<class T>
class Greater{
public:
bool operator()(const T& t1, const T& t2) const
{
return t1 > t2;
}
};
namespace czh{
template<class T, class Container = vector<T>, class Compare = Greater<T>>
class priority_queue {
public:
priority_queue() = default;
/*priority_queue()
{}*/
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
:_con(first, last)
{
//利用向下调整算法,从下到上建堆
/*for (int i = (_con.size() - 2) / 2; i >= 0; i--)
{
AdjustDown(i);
}*/
利用向上调整算法,从上到下调整
for (size_t i = 1; i < _con.size(); i++)
{
AdjustUp(i);
}
}
void push(const T& data)
{
_con.push_back(data);
// 向上调整
AdjustUp(_con.size() - 1);
}
void pop()
{
if (empty()) return;
swap(_con.front(),_con.back());
_con.pop_back();
AdjustDown(0);
}
size_t size() const
{
return _con.size();
}
const T& top() const
{
return _con.front();
}
bool empty() const
{
return _con.empty();
}
private:
//向上调整算法
void AdjustUp(int child)
{
Compare com;
int parent = (child - 1) >> 1;
while (child > 0)
{
/*if (_con[child] > _con[parent])*/
if (com(_con[child], _con[parent]))
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
{
break;
}
}
}
void AdjustDown(int parent)
{
Compare com;
size_t child = 2 * parent + 1;
while (child < _con.size())
{
/*if (child + 1 < _con.size() && _con[child + 1] < _con[child])*/
if (child + 1 < _con.size() && com(_con[child + 1], _con[child]))
{
child++;
}
/*if (_con[child] > _con[parent])*/
if (com(_con[child], _con[parent]))
{
swap(_con[child], _con[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
//向下调整算法
Container _con;
};
}
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
上面介绍的3种数据结构都是容器适配器(container adapter)。