栈(stack)是一种先进后出的数据结构
内部api接口:
top();//获取栈顶元素值
push();//放入元素到栈顶
pop();//将栈顶元素弹出
size();//元素个数
isEmpty();//是否为空
接口实现
使用向量(动态数组/顺序表)实现
将tail初始化为0,每增加一个元素,tail++,删去一个元素tail--,tail-1即为栈顶元素
用size来记录元素个数
#include<iostream>
#include<vector>
using namespace std;
template<typename T>
class Stack{
private:
vector<T>ves;
int size;
int tail;
int capacity;
public:
void push(T const&e){
if(size>=capacity){
ves.reserve(capacity*2);
capacity = capacity*2;
}
size++;
ves[tail] = e;
tail++;
}
Stack():size{0},tail{0},capacity{5}{
ves.reserve(capacity);
}
bool isEmpty(){
return size==0;
}
bool pop(){
if(isEmpty())return false;
tail--;
size--;
return true;
}
T top(){
if(!isEmpty()){
return ves[tail-1];
}
return -1;
}
};
int main()
{
Stack<int> s;
for(int i = 0;i<5;i++){
s.push(i);
}
while(!s.isEmpty()){
cout<<s.top()<<endl;
s.pop();
}
cin.get();
return 0;
}
逆序输出类问题
1. 进制转换
短除法:
每次整除要转换的进制数,将其余数记录,并继续整除其商
最后自底向上将余数记录,即为转换后的值
//base为进制,n为要转换的整数
void convert(stack<char>&s,int n,int base){
static char digit[] = {'0','1','2','3','4','5','6','7','8','A','B','C','D','E','F'};
while(n){
s.push(digit[n%base]);//余数入栈。余数的值对应digit数组中的值
n/=base;
}
}
递归嵌套类问题
1. 括号匹配
消去一对紧邻的左右括号,不会影响全局的匹配判断
即为:[L] ( ) [R]匹配。仅当[L] [R]匹配
顺序扫描表达式,用栈记录已经扫描的部分,遇到左括号就入栈,遇到右括号且栈顶匹配就将栈顶出栈,这样消去的匹配的一对就是紧邻的一对左右括号
如果最后一个右括号被扫描到之前,栈变空,或扫描后,栈非空,结果都表示括号不匹配
遍历后,栈空当且仅当匹配
bool isValid(string s){
stack<char>c;
for(int i = 0;i<s.size();i++){
if(s[i]=='('||s[i]=='{'||s[i]=='['){ //遇到左括号则进栈
c.push(s[i]);
}
else{ //如果s[i]为右括号
if(c.empty()) return false; //如果遇到右括号且此时栈已空
if((c.top()=='('&&s[i]!=')')||(c.top()=='['&&s[i]!=']')||(c.top()=='{'&&s[i]!='}')){ //如果是某种右括号且与对应的栈顶匹配,则栈顶出栈
return false;
}
c.pop();
}
}
return c.empty();
}
如果是判断单种括号,可以使用一个计数器,在遇到左括号时+1,遇到右括号且此时计数器非>0时,计数器-1,直到最后计数器是否为0,大体结构与使用栈判断相同
但是计数器的方法不能用于多种括号并存的情况
包含min/max函数的栈(最小/最大栈)
设计一个支持push,pop,top,getMin操作的栈
思路:使用两个栈,令第二个栈在栈顶永远保存的是此时第一个栈中的最小元素值
两个栈的元素是一一对应的,出栈是同时将栈顶出栈
但是入栈时,有不同之处,例如:
入栈序列 -2 0 -3
第一个栈: -3 0 -2] 第二个栈由于始终在栈顶保存当前栈最小值: -3 -3 -2]
主要考察的是getMin方法的实现,所以直接用标准库中的栈也可以
class MinStack {
private:
stack<int>s1;
stack<int>s2;
public:
MinStack() {}
void push(int val) {
s1.push(val);
if(s2.empty()){
s2.push(val);
}
else{
if(val<=s2.top()) s2.push(val);
else s2.push(s2.top());
}
}
void pop() {
s1.pop();
s2.pop();
}
int top() {
return s1.top();
}
int getMin() {
return s2.top();
}
};
栈的混洗/判断栈的压入弹出序列是否正确
栈的输入,弹出序列
如果输入序列为n个元素,则混洗序列个数可能最多有:
k的取值为1-n
因为1这个元素可能是第一个被推入左侧栈中的元素,最极端也可能是第n个
上述的栈混洗的个数即为catalan(n) = (2n)!/((n+1)!*n!)
如何判断一个弹出序列是输入序列的一个栈混洗
上图的条件称为禁形
是一个序列是否为栈混洗的充分必要条件
判断方式:
引入三个栈,模拟栈的混洗过程,每次s.pop之前,检测s是否为空,或者需要弹出的元素在s中并非栈顶元素
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
//如果扫描完popped之后,栈的全部元素都弹出,即栈为空,则返回true,这是栈的一个弹出序列
// 根据popped序列从左到右的顺序,决定栈的弹出和放入操作
// 如果当前push之后,栈顶元素不==poped[i],则继续push,如果等于则pop,且i++
// 去判断下一个出栈早的元素
stack<int>s;
int j = 0;
for(int i = 0;i<popped.size();i++)
{
s.push(pushed[i]);
while(!s.empty()&&s.top()==popped[j]){//因为可能push进了很多元素,但是直到某一元素才和popped左侧相等,这个元素弹出后,之前的元素又和popped更右侧一个相等 //j不能越界,这里要用到&&运算符一边错误则全错的特性
s.pop();
j++;
}
}
if(s.empty())return true;
else return false;
}
};
一个重要性质:n对括号所能构成的合法表达式的个数 = n个元素的栈混洗个数
逆波兰表达式求值/中缀表达式求值
(1)直接对中缀表达式进行求值
需要使用两个栈:一个放置运算数,一个放置运算符
(2)RPN 逆波兰表达式
相比中缀表达式的有点:不适用括号即可表示带优先级的运算关系
:运算符在表达式序列中谁先出现则谁优先计算
若表达式处理完毕且表达式正确则栈最后只剩一个元素即为结果
将操作数一次入栈,遇到操作符则将需要的操作数依次出栈,并将运算结果再次入栈
(1)首先要将中缀表达式转换为后缀表达式:
所有操作数在后缀和中缀表达式中的相对次序是不变的
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int>s;
for(int i = 0;i<tokens.size();i++){
if(tokens[i]=="+"||tokens[i]=="-"||tokens[i]=="*"||tokens[i]=="/"){
int a = s.top();
s.pop();
int b = s.top();
s.pop();
if(tokens[i]=="+") s.push(a+b);
if(tokens[i]=="-") s.push(b-a);
if(tokens[i]=="*") s.push(b*a);
if(tokens[i]=="/") s.push(b/a);
}
else {
s.push(stoi(tokens[i]));
}
}
return s.top();
}
};
单调栈
Leetcode 739. 每日温度
维护一个从始至终都为单调递减的栈(从栈底到栈顶)
只不过这里压入栈的是每天温度的下标
如果当前扫描到的元素大于栈顶元素,这个元素即为此序列中第一次大于栈顶的元素
所以下标相减,就是等待的天数
如果小于栈顶,则将下标入栈
具体的图解:
LeetCode 图解 | 739.每日温度 - 每日温度 - 力扣(LeetCode) (leetcode-cn.com)
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int>ans(n,0);
stack<int>s;
for(int i = 0;i<n;i++){
while(!s.empty()&&temperatures[i]>temperatures[s.top()]){
ans[s.top()] = i-s.top();
s.pop();
}
s.push(i);
}
return ans;
}
};