文章目录
- Stack
- 最小栈
- 栈的弹出压入序列
- 逆波兰表达式求值
- 用栈实现队列
- 模拟实现
- queue
- 用队列实现栈
- 模拟实现
Stack
stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。它的使用和之前学习的vector等容器类似,都具有插入、删除、判空功能,具体的实现就做几道OJ题来感受一下,也可以查看文档进行了解。
最小栈
因为栈的性质是后进先出,所以可以利用这个性质。首先创建两个栈,一个用于压栈(s1),一个用于记录较小元素的栈(s2)。首先每一次压栈s1都会将元素保存,如果s2栈顶的元素比最近压栈的元素大或者当s2为空时,则s2将元素入栈。这样操作就能保持每一次s2里面最近一次进栈的元素都是以压栈元素中最小的元素,此时我们只需要每次取出s2栈顶的元素就是最小元素。如果s1进行出栈操作,那么如果s2的栈顶元素等于s1出栈的元素,那么s2也要进行出栈,这样才能保持住s2的栈顶元素是最小的。
class MinStack {
public:
MinStack() {
}
void push(int val) {
s1.push(val);
if(s2.empty() || s2.top() >= val)
s2.push(val);
}
void pop() {
if(s1.top() == s2.top())
s2.pop();
s1.pop();
}
int top() {
return s1.top();
}
int getMin() {
return s2.top();
}
private:
stack<int> s1;
stack<int> s2;
};
栈的弹出压入序列
这道题考验的就是我们对于后进先出性质的应用,首先一定得判断一下两个序列的长度是否一样,如果不一样那就肯定不对了。接着我们可以对入栈序列进行依次入栈,每一次入栈的元素都和出栈序列的元素比较,如果相等那么出栈序列的比较元素就往后一个,刚入栈的元素就出栈。这样依次反复循环如果栈里的元素全部出栈完并且此时出栈序列所有元素都比较完即为匹配成功。反之其他情况匹配失败
class Solution {
public:
bool IsPopOrder(vector<int> pushV,vector<int> popV) {
if(pushV.size() != popV.size())
return false;
//创建栈和记录两个序列的比较个数
int out = 0;
int in = 0;
stack<int> s;
//如果出栈序列比较完即推出训话
while(out < popV.size()){
//如果栈为空或者栈顶元素不等于出栈序列的比较元素则继续入栈
while(s.empty() || s.top() != popV[out]){
if(in < pushV.size())
s.push(pushV[in++]);
//如果全部元素都入栈但出栈序列没有比较完则匹配失败
else
return false;
}
//如果栈顶元素等于出栈序列比较元素则出栈
s.pop();
++out;
}
return true;
}
};
逆波兰表达式求值
逆波兰表达式简单理解就是将运算符放到运算数的后面,因此这道题可以使用栈来完成。首先如果遇到数字就入栈,遇到运算符就不用入栈了,用两个变量来接受栈的两个元素进行运算之后再把结果入栈,这样每一次的运算结果都是栈顶元素,依次循环直至结束。此时栈顶的元素就是最后的运算结果。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for (auto e : tokens) {
if (e == "+" || e == "*" || e == "-" || e == "/") {
int right = st.top();
st.pop();
int left = st.top();
st.pop();
switch (e[0]) {
case '+':
st.push(left + right);
break;
case '-':
st.push(left - right);
break;
case '*':
st.push(left * right);
break;
case '/':
st.push(left / right);
break;
}
}
else
//需要将字符串转为整型才可以入栈
st.push(stoi(e));
}
return st.top();
}
};
用栈实现队列
这道题如果用C语言写就十分的不方便,什么都得自己造。但是用C++有了STL之后就十分的轻松。思路很简单,用两个栈实现队列的先进先出性质。
class MyQueue {
public:
MyQueue() {
}
void push(int x) {
ins.push(x);
}
int pop() {
if(outs.empty()){
while (!ins.empty()) {
outs.push(ins.top());
ins.pop();
}
}
int x = outs.top();
outs.pop();
return x;
}
int peek() {
if(outs.empty()){
while (!ins.empty()) {
outs.push(ins.top());
ins.pop();
}
}
int x = outs.top();
return x;
}
bool empty() {
return ins.empty() && outs.empty();
}
private:
stack<int> outs;
stack<int> ins;
};
模拟实现
stack其实是一种适配器设计模式,它的接口实现和vector非常的相像,因此我们可以直接使用vector去实现stack
#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<stack>
using namespace std;
namespace my {
template<class T, class con = vector<T>>
class stack {
public:
void push(const T& x) {
_con.push_back(x);
}
void pop() {
_con.pop_back();
}
const T& top() {
return _con.back();
}
bool empty() {
return _con.empty();
}
private:
con _con;
};
}
queue
队列和栈其实都是一样的接口和功能,不同的是队列是一种先进先出的容器,和栈刚好相反。还有就是栈的底层是数组而队列的底层是链表,因此queue多了返回队头和队尾的接口。
用队列实现栈
这个和上面的用栈实现队列是一样的,都是为了模拟出各自的性质,所以这里也可以用两个队列去实现。
class MyStack {
public:
MyStack() {
}
void push(int x) {
//元素先入第二个队列,保证最先入队列的元素在队头
q2.push(x);
//将原队列的所有元素依次入第二个队列,保证队列结构
//再进行交换
while(!q1.empty()){
q2.push(q1.front());
q1.pop();
}
swap(q1, q2);
}
int pop() {
int x = q1.front();
q1.pop();
return x;
}
int top() {
return q1.front();
}
bool empty() {
return q1.empty();
}
private:
queue<int> q1;
queue<int> q2;
};
模拟实现
queue也是一种适配器设计模式,所以也可以用已有的容器去实现它,因为queue的底层实现是链表,所以我们用list去实现
#pragma once
#include"stack.h"
namespace my2 {
template<class T, class con = list<T>>
class queue {
public:
void push(const T& x) {
_con.push_back(x);
}
void pop() {
_con.pop_front();
}
const T& top() {
return _con.front();
}
bool empty() {
return _con.empty();
}
private:
con _con;
};
}