栈
栈和队列不是容器而是容器适配器
栈和队列的使用
栈不支持迭代器,为了支持栈的先进后出,后进先出。
void test_stack()
{
stack<int> s;
s.push(1);
s.push(2);
s.push(3);
s.push(4);
while (!s.empty())
{
cout << s.top() << " ";
s.pop();//头删
}
}
例题设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
思路:我们利用c++的栈,设计出两个栈st和minST,st插入一个val,如果这时候minST为空,或者这个val小于等于minST.top(),minST就插入这个val,st删除的时候,如果被删除的val跟minST.top()一样,minST也pop.
class MinStack {
public:
MinStack() {
}//不需要我们自己写构造和析构,默认的就行了
void push(int val) {
st.push(val);
if(minST.empty()||minST.top()>=val)
{
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> st;
stack<int> minST;
};
牛客网链接
拿入栈顺序去模拟出栈的顺序,如果可以模拟出来就行,不能就不行
- 入栈位置和出栈位置的值不相等,那么这个值需要先入,后面再出。
- 如果入栈位置的值和出栈位置的值相等,这个值还是入栈,持续出数据,直到栈为空或者栈顶的元素跟出栈序列的值不匹配。
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> st;
size_t pushi=0,popi=0;
while(pushi<pushV.size())
{
st.push(pushV[pushi]);
pushi++;
while(!st.empty()&&st.top()==popV[popi])
{
st.pop();
popi++;
}
}
if(st.empty())
{
return true;
}
else
{
return false;
}
}
};
例题:
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
链接
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(const auto& str: tokens)//把token每一个数据给赋值给str
{
if(str=="+"||str=="-"||str=="*"||str=="/")//如果是运算符就计算左右两个操作数
{
int right=st.top();//一定要分左右,不然乘除就有问题
st.pop();
int left=st.top();
st.pop();
switch(str[0])//str[0]就对应操作符
{
case '+':
st.push(left+right);//再把数据push
break;
case '-':
st.push(left-right);
break;
case '*':
st.push(left*right);
break;
case '/':
st.push(left/right);
break;
}
}
else
{
st.push(stoi(str));//stoi把字符串转化成整数
}
}
return st.top();
}
};
队列同样不支持迭代器,为了支持队列的先进先出,后进后出。
队列和栈的实现
#pragma once
#include<vector>
#include<list>
namespace cl
{
template<class T,class Container=deque<T>>
class queue
{
public:
bool empty()const
{
return _con.empty();
}
size_t size()const
{
return _con.size();
}
const T& front()const
{
return _con.front();
}
const T& back()const
{
return _con.back();
}
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
private:
Container _con;
};
//适配器模式
template<class T,class Container>//r
class stack
{
public:
bool empty()const
{
return _con.empty();
}
size_t size()const
{
return _con.size();
}
const T& top()const
{
return _con.back();
}
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
private:
Container _con;
};
void test_stack()
{
stack<int,std::vector<int>> s;
s.push(1);
s.push(2);
s.push(3);
s.push(4);
while (!s.empty())
{
cout << s.top() << " ";
s.pop();
}
cout << endl;
}
void test_queue()
{
//queue<int> q;
queue<int,list<int>> q;
//queue<int, vector<int>> q;//会报错
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty())//队列是先进先出
{
cout << q.front() << " ";//取队尾
q.pop();
}
cout << endl;
}
}
适配器模式
适配器是一种设计模式(设计模式是一套被反复使用的,多数人知晓迭代,经过分类编目的,代码设计经验的总结),该模式是将一个类的接口转换成客户希望的另一个接口
deque
deque
是双端队列,融合vector和list的优点
vector
优点:下标随机访问,尾插尾删效率高
缺点:扩容,不适合头插头删
list
优点:按需申请释放空间,任意位置O(1)插入删除
缺点:不支持下标随机访问
deque:适合头尾的插入删除
如果没有deque,stack的实现用vector和list
默认适配
queue用list
默认适配
相比vector的优势:扩容代价不打,不需要拷贝数据,浪费空间不多
相比list的优势:cpu高速cache命中,其中不会频繁申请小块空间,申请和释放空间次数少,代价低
deque做为stack和queue是容器是非常合适的
deque适合头尾插入删除,但是中间插入删除,和随机访问效率都差强人意,所以高频访问还是vector,任意位置插入删除还是list