stack
栈(Stack)是一种运算受限的线性表 ,只允许在一端进行插入或删除操作。这一端被称为栈顶 (Top),相对地,另一端称为栈底 (Bottom)。栈的特点是后进先出(Last In First Out,简称LIFO),即最后插入的元素最先被删除。
基本操作
push(): 在栈顶添加一个元素。pop(): 移除栈顶元素。top(): 返回栈顶元素的引用,但不移除它。empty(): 检查栈是否为空。size(): 返回栈中元素的数量。
cpp
stack<int> st;
st.push(1);
st.push(2);
st.push(3);
st.pop();
cout<<st.top();
st.empty();
cout<<st.size();
模拟实现
cpp
#pragma once
namespace Milestone
{
template<class T,class Container = vector<int>>//给缺省值
class stack {
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
const T& Top()const
{
return _con.back();
}
size_t size()const
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
}
queue
它允许在一端添加元素(称为队尾),并在另一端移除元素(称为队首)。
队列是一种线性数据结构,它遵循以下规则:
- 元素只能从队尾添加。
- 元素只能从队首移除。
基本操作
empty(): 检查队列是否为空。size(): 返回队列中的元素数量。front(): 返回队首元素的引用。back(): 返回队尾元素的引用。push(): 在队尾添加一个元素。pop(): 移除队首元素。
cpp
queue<int> st;
queue.push(1);
queue.push(2);
queue.push(3);
queue.pop();
cout<<queue.front();
cout<<queue.back();
queue.empty();
cout<<queue.size();
模拟实现
cpp
#pragma once
namespace Milestone {
template<class T,class Container = list<int>>
class queue {
public:
void push(const T& x)
{
_con.push(x);
}
void pop()
{
_con.pop();
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
T& front()const
{
return _con.front();
}
T& back()const
{
return _con.back();
}
private:
Container _con;
};
}
priority_queue
- 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素
中最大的。 - 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶
部的元素)。 - 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue
提供一组特定的成员函数来访问其元素。元素从特定容器的"尾部"弹出,其称为优先队列的
顶部。 - 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过
随机访问迭代器访问
基本操作
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
cpp
priority_queue<int> pq;
pq.push(1);
pq.push(2);
pq.empty();
pq.pop();
cout<<pq.top();
模拟实现
cpp
#pragma once
namespace Milestone
{
//默认是大堆
template<class T,class Container = vector<T>>
class priority_queue {
public:
void adjustup(size_t child)//有隐式this
{
size_t parent = (child - 1) / 2;
while (child > 0)
{
if (_con[child] > _con[parent])
{
swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
}
}
void push(const T& x)
{
_con.push_back(x);
adjustup(_con.size() - 1);
}
void adjustdown(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < _con.size())
{
if (child + 1 < _con.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 pop()
{
swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
adjustdown(0);
}
const T& top()
{
return _con[0];
}
size_t size()const
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
}
容器适配器
什么是容器适配器
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设
计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
其中SLT的容器适配器
stack:栈适配器
queue:队列适配器
priority_queue:优先队列适配器
在上述中,stack与queue均选择deque为默认容器
为什么选择deque
**deque(双端队列):是一种双开口的"连续"**空间的数据结构,双开口的含义是:可以在头尾两端
进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与
list比较,空间利用率比较高。
- stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进
行操作。- 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的
元素增长时,deque不仅效率高,而且内存使用率高。

vector list deque的优缺点
vector
Vecaor
优点;
1.尾插尾删效率高,支持下标随机访问
2.物理空间连续,所以高速缓存利用率高
缺点:
1.空间需要扩容,扩容有代价(效率和空间)
2.头部和中间插入数据慢
list
优点:
1.按需申请空间,不需要释放空间
2.任意位置插入删除
缺点
1.不支持下标随机访问
deque
优点
1.头插尾插插入效率比vector和list高
2.下标随机访问不错,但相比vector逊
缺点:
1中间插入删除效率低,要挪动数据,为O(N)