🍅一文包教会,不再赘述栈最基本的结构和性质(栈的基本介绍在这里)
(队列基本介绍和实现),博主主页还有很多栈和队列oj题哦~
目录
☃️1.stack_list
🐝1.1 介绍
🐝1.2 stack和list实现
🐝1.3 oj
☃️2.deque
☃️1.stack_list
🐝1.1介绍
首先明确 stack 不是容器,而是容器适配器,并且他的接口函数我们一看就懂
并且发现栈没有迭代器,其实这也很正常,如果有迭代器那就可以随意访问,随意增删,不符合栈后进先出的性质
设计模式的概念:有点类似于兵法,很多人写了很多代码之后意识到什么场景有怎样的固定格式,从而形成了一种设计模式
适配器模式:用现有的东西,封装转换出你想要的东西
迭代器模式:不暴露底层的细节,访问数据结时封装后用统一方式访问容器
🐝1.2 stack和list实现
那么上面说过stack是一种适配器模式
所以我们可以考虑用vector或者list转换出stack
直接在模板里面加上一个参数表示你使用的是什么(vector/list)
list的实现也是一样的用两个参数写模板,不再赘述
🐝1.3 oj
最小栈
首先分析一下,如果用一个变量记录最小元素,不能实时更新,万一最小元素被pop,无法找到第二小的元素,所以我们只需要用一个数据结构保存较小元素,最好就是stack,当保存最小元素的栈sortedStack为空,把要来的元素push,或者要来的元素小于sortedStack栈顶元素,也push,最后sortedStack栈顶的元素就是最小的,一旦最小元素被pop,我们同时把sortedStack栈顶pop,此时最新的栈顶就是次小元素啦,当然想用其他数据结构比如vector也是可以的,但是栈最好
class MinStack {
public:
/** initialize your data structure here. */
stack<int> dataStack; // 数据栈
stack<int> sortedStack; // 有序栈(栈底最大,栈顶最小,有 <= 栈顶的元素才进行push)
MinStack()
{}
void push(int val) {
dataStack.push(val);
// --有序栈
// ----如果为空 则 push
// ----如果val 不大于 当前栈顶 则 push
if(sortedStack.empty() || val <= sortedStack.top())
sortedStack.push(val);
}
void pop() {
// 数据栈的栈顶如果与 有序栈的栈顶相等, 那么都出栈
if(dataStack.top() == sortedStack.top())
sortedStack.pop();
dataStack.pop();
}
int top() {
return dataStack.top();
}
int getMin() {
return sortedStack.top();
}
};
栈的压入、弹出序列
1.入栈序列入栈
2.判断 当 栈不是空&&栈顶元素==弹出序列当前元素(用一个下标i,每次执行1.i不变),删除栈顶元素,i++
3.判断最后i==弹出序列的size()
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
stack<int> push;
int i=0;
for(auto e:pushV)
{
push.push(e);
while(!push.empty()&&push.top()==popV[i])
{
push.pop();
++i;
}
}
return i==popV.size();
}
};
逆 波兰表达式求值
区分中缀和后缀的区别
题目里给了较为清楚的解释
tokens = ["4","13","5","/","+"]
对于这个用例,首先数字一直push到栈,一旦遇到运算符,栈顶元素作为操作符右侧数字,pop(),此时栈顶元素是操作符左侧数字,pop(),然后把远算结果push到栈
public:
int evalRPN(vector<string>& tokens) {
//首先写一个栈存储数据
stack <int> st;
for(auto e:tokens)
{
if(e=="*" || e=="+"||e=="-"||e=="/")
{
int num2=st.top();
st.pop();
int num1=st.top();
st.pop();
switch (e[0]) {
case '+':
st.push(num1 + num2);
break;
case '-':
st.push(num1 - num2);
break;
case '*':
st.push(num1 * num2);
break;
case '/':
st.push(num1 / num2);
break;
}
}
else
{
st.push(stoi(e));
}
}
return st.top();
}
};
☃️2.deque
他是一个双端队列,不需要满足先入先出,可以完美兼容,融合vector和list的缺点
但是他还有缺点,并且实际上使用的不太多
缺点:
想在deque里面随机访问怎么搞?
算在第几个Buf里面然后再算在第几个,有一定的消耗但是没有vector快
一个Buf里插入还有一定的消耗,但是没有list快
优点:
但是做栈的默认容器很不错,做队列的默认容器也很好
什么情形比较好用?中间插入删除少,头尾插入删除多,偶尔下标随机访问