stack和queue
stack
和queue
是一种容器适配器,stack
满足后进先出的规则,queue
满足先进先出的规则。两者都是只能从容器的一端插入和提取操作。
容器适配器
适配器是一种设计模式,该种模式是将一个类的接口转换成客户希望的另一个接口。
- STL标准库中,
stack
和queue
底层都是使用deque
。
deque
deque
是双端队列,头尾都可插入删除,且时间负责度都是O(1),与vector
相比,头插效率高,不需要移动数据;与list
相比,空间利用率比较高。
deque
并不是真正连续的空间,而是由一段段连续的小空间拼接而成,实际上deque
类似于一个动态的二维数组。
deque的优点和缺点
优点
- 与
vector
相比,头插头删效率高,不需要移动数据;扩容时也不需要移动大量数据。 - 与
list
相比,由于deque
底层是连续空间,空间利用率比较高,不需要存储额外字段。
缺点
- 不适合遍历,因为遍历时,
deque
的迭代器要频繁去检测是否移动到某段小空间的边界,导致效率底下。
stack和queue的模拟实现
namespace max {
template<class T, class Container = deque<T>>
class stack {
public:
void push(const T &val) {
_a.push_back(val); // deque尾插
}
void pop() {
_a.pop_back(); // deque尾删来达到后进先出
}
T &top() {
return _a.back(); // 返回队尾元素即栈的头元素
}
size_t size() {
return _a.size();
}
bool empty() {
return _a.empty();
}
private:
Container _a;
};
}
namespace max {
template<class T, class Container = deque<T>>
class queue {
public:
void push(const T &val) {
_a.push_back(val);
}
void pop() {
_a.pop_front(); // 头删来达到先进先出
}
T &front() {
return _a.front();
}
size_t size() {
return _a.size();
}
bool empty() {
return _a.empty();
}
private:
Container _a;
};
}
优先级队列
优先级队列
priority_queue
是一种容器适配器,STL标准库中,它的第一个元素总是所有元素中最大的,即大根堆。
模拟实现
- 要确保堆的实现,插入元素时在队尾插入,向上调整保证大根堆。
void adjust_up(int child) {
int parent = (child - 1) / 2;
while (child > 0) {
if (_a[child] > _a[parent]) {
std::swap(_a[parent], _a[child]);
} else {
break;
}
child = parent;
parent = (child - 1) / 2;
}
}
- 删除元素为保证先进先出原则,需要删除队列首元素,因此交换首尾元素,再使用
pop_back()
,向下调整确保大根堆。
void adjust_down(int parent) {
size_t child = parent * 2 + 1;
while (child < _a.size()) {
if (child + 1 < _a.size() && _a[child + 1] > _a[child]) {
child++;
}
if (_a[child] > _a[parent]) {
std::swap(_a[child], _a[parent]);
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
}
仿函数
- 根据上述代码进行向下调整向上调整,只能得到大根堆,是固定写死的方式。如果需要得到小根堆,需要对代码进行修改,比较麻烦。
- STL标准库中提供的
priority_queue
有一个模板类。
class Compare = less<typename Container::value_type>
- 可以把它理解为一个比较器,默认传入的是判断
<
的比较器。
仿函数就是使一个类的使用看上去像一个函数,其实现就是类中实现一个
operator()
,这个类就有了类似函数的行为,就是一个仿函数类了。
template<class T>
class Less {
public:
bool operator()(const T &x, const T &y) {
return x < y;
}
};
template<class T>
class Greater {
public:
bool operator()(const T &x, const T &y) {
return x > y;
}
};
- 在类模板中,添加仿函数类模板。
template<class T, class Container = vector<T>, class Compare = Less<T>>
Compare _com; // 仿函数对象
- 就可以使用仿函数来控制大小比较。
完整实现
namespace max {
// 仿函数
template<class T>
class Less {
public:
bool operator()(const T &x, const T &y) {
return x < y;
}
};
template<class T>
class Greater {
public:
bool operator()(const T &x, const T &y) {
return x > y;
}
};
template<class T, class Container = vector<T>, class Compare = Less<T>>
class priority_queue {
public:
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
: _a(first, last) {
for (int i = (_a.size() - 1 - 1) / 2; i >= 0; i--) {
adjust_down(i);
}
}
void push(const T &val) {
_a.push_back(val);
adjust_up(_a.size() - 1);
}
void adjust_up(int child) {
int parent = (child - 1) / 2;
while (child > 0) {
if (_com(_a[parent], _a[child])) {
std::swap(_a[parent], _a[child]);
} else {
break;
}
child = parent;
parent = (child - 1) / 2;
}
}
void pop() {
std::swap(_a[0], _a[_a.size() - 1]); // 首尾互换
_a.pop_back();
// 向下调整
adjust_down(0);
}
void adjust_down(int parent) {
size_t child = parent * 2 + 1;
while (child < _a.size()) {
if (child + 1 < _a.size() && _com(_a[child], _a[child + 1])) {
child++;
}
if (_com(_a[parent], _a[child])) {
std::swap(_a[child], _a[parent]);
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
}
size_t size() {
return _a.size();
}
bool empty() {
return _a.empty();
}
const T &top() const {
return _a[0];
}
private:
Container _a;
Compare _com;
};
}