目录
stack
简介
1、stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行 元素的插入与提取操作。
2、 stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定 的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下 操作:
- empty:判空操作
- back:获取尾部元素操作
- push_back:尾部插入元素操作
- pop_back:尾部删除元素操作
4. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque。
使用
成员函数 | 功能 |
---|---|
empty | 判断栈是否为空 |
size | 获取栈中有效元素个数 |
top | 获取栈顶元素 |
push | 元素入栈 |
pop | 元素出栈 |
swap | 交换两个栈中的数据 |
模拟实现
namespace cl //防止命名冲突
{
template<class T, class Container = std::deque<T>>
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();
}
//交换两个栈中的数据
void swap(stack<T, Container>& st)
{
_con.swap(st._con);
}
private:
Container _con;
};
}
queue
简介
1.队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端 提取元素。
2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的 成员函数来访问其元素。元素从队尾入队列,从队头出队列。
3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操 作: empty:检测队列是否为空 size:返回队列中有效元素的个数 front:返回队头元素的引用 back:返回队尾元素的引用 push_back:在队列尾部入队列 pop_front:在队列头部出队列
4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标 准容器deque。
使用
成员函数 | 功能 |
---|---|
empty | 判断队列是否为空 |
size | 获取队列中有效元素个数 |
front | 获取队头元素 |
back | 获取队尾元素 |
push | 队尾入队列 |
pop | 队头出队列 |
swap | 交换两个队列中的数据 |
模拟实现
namespace cl //防止命名冲突
{
template<class T, class Container = std::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();
}
//交换两个队列中的数据
void swap(queue<T, Container>& q)
{
_con.swap(q._con);
}
private:
Container _con;
};
}
deque
简介
我们通过看文档会发现这个名为双端队列的容器居然如此强大,不仅弥补了vector和list的缺点,更是都具有vector和list的优点。当然,我们也知道,它肯定不会是无敌的存在,那么我们就不会再学习vector和list了。
它作为我们实现stack和queue的缺省参数,肯定有它的存在意义。
它通过一个中控指针数组来完成了很多效率很低的操作!
dqueue的优势
在实现stack与vector相比:
扩容代码不大,不需要拷贝数据浪费空间也不多。
在实现stack与list相比:
cpu高速cache命中。其次不会频繁申请小块空间。申请和释放空间次数少代价低。
在实现queue与list相比:
cpu高速cache命中。其次不会频繁申请小块空间。申请和释放空间次数少代价低。
总结:
deque适合头尾插入删除,但是中间插入删除,和随机访问效率都差强人意。所以要高频随机访问还得是vector,要任意位置插入删除,还得是list。
相关OJ题目
解析见代码中的注释!
逆波兰表达式求值
力扣https://leetcode.cn/problems/evaluate-reverse-polish-notation/
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(const auto& str : tokens)
//利用范围for进行遍历
{
if(str=="+"||str=="-"||str=="*"||str=="/")
//如果是操作符就进行操作
{
long right=st.top();
st.pop();
long left=st.top();
st.pop();
switch(str[0])
//这里不能填写str因为switch只能支持整型
//因此直接取它的第一个字符
{
case '+':
st.push(left+right);
break;
case '-':
st.push(left-right);
break;
case '*':
st.push(left * right);
break;
case '/':
st.push(left/right);
break;
default :
assert(false);
}
}
else
{
st.push(stoi(str));
//将tokens中的字符串转为int型存放入栈中
}
}
return st.top();
//最终栈中剩下的数就是我们所需要的
}
};
栈的压入、弹出序列
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> st;
//定义一个栈用来实现
size_t pushi=0,popi=0;
//分别用来遍历pushV和popV
while(pushi<pushV.size())
{
st.push(pushV[pushi]);
//将第一个vector中的数据入栈
pushi++;
//更新pushi
while(!st.empty()&&st.top()==popV[popi])
{
st.pop();
popi++;
}
}
return popi==popV.size();
}
};
最小栈
力扣https://leetcode.cn/problems/min-stack/
class MinStack {
public:
MinStack()
{}
void push(int val) {
St.push(val);
if(minSt.empty()||val<=minSt.top())
{
minSt.push(val);
}
}
void pop() {
if(St.top()==minSt.top())
{
minSt.pop();
}
St.pop();
}
int top() {
return St.top();
}
int getMin() {
return minSt.top();
}
private:
stack<int> minSt;
stack<int> St;
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(val);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/