栈
1. 简要介绍
栈就像叠盘子,先进后出;队列就像排队,先进先出。栈既可以用数组来实现,也可以用链表来实现。用数组实现的栈,叫作顺序栈,用链表实现的栈,叫作链式栈。
常用操作:
(1)数据存取:
push(elem);
//向栈顶添加元素pop();
//从栈顶移除第一个元素top();
//返回栈顶元素
(2)大小操作:
empty();
//判断堆栈是否为空size();
//返回栈的大小
2. Leetcode练习题
leetcode上关于栈的题目:20,155,232,844,224,682,496.
2.1 20.有效的括号
分析:遇到能匹配的括号就pop。
class Solution {
public:
bool isValid(string s) {
vector<char> v;
//如果是奇数一定是无效字符串
if(s.length() % 2 == 1) return false;
//最简单的思路
for(char c : s) {
if(c == '(' || c == '[' || c == '{') {
v.push_back(c);
}
if(v.empty()) return false; //都没有左括号,直接返回false
if(c == ')') {
if(v.back() != '(') return false;
v.pop_back();
}
else if (c == ']') {
if(v.back() != '[') return false;
v.pop_back();
}
//如果用else if就不会出错,如果用else就会出错
else if (c == '}'){
if(v.back() != '{') return false;
v.pop_back();
}
}
return v.empty() ? true : false;
}
};
2.2 155.最小栈
分析:我觉得目前看到的最好且最简洁的一个思路就是用pair来存储数据,每次加入新的数据的时候都进行一次比较,让栈顶元素的second值为最小值,直接可以返回最小值。
class MinStack {
public:
MinStack() {
}
void push(int x) {
if(st.empty()) {
st.push({x, x});
} else {
st.push({x, min(x, st.top().second)});
}
}
//pop的时候在非空栈上操作
void pop() {
st.pop();
}
int top() {
return st.top().first;
}
int getMin() {
return st.top().second;
}
private:
// 用一个pair来存储,第一个数存储值,第二个数存储stack中最小的数,
// 每次加入新的数的时候进行一次比较,也就是O(1)
stack <pair<int, int>> st;
};
2.3 232.用栈实现队列
题目链接:232. 用栈实现队列 - 力扣(LeetCode)
分析:用两个栈实现队列,画个图就能很好理解了,我觉得还是画图最方便。
class MyQueue {
public:
//栈的操作:push pop top
stack<int> stIn;
stack<int> stOut;
MyQueue() {
}
void push(int x) {
stIn.push(x);
}
int pop() {
if(stOut.empty()) {
while(! stIn.empty()) {
stOut.push(stIn.top());
stIn.pop();
}
}
int ret = stOut.top();
stOut.pop();
return ret;
}
int peek() {
int ret = this->pop(); //直接用已经定义好的
stOut.push(ret); //记得push回去
return ret;
}
bool empty() {
return stIn.empty() && stOut.empty();
}
};
2.4 844.比较含退格的字符串
题目链接:844. 比较含退格的字符串 - 力扣(LeetCode)
分析:因为字符串是否相等可以直接用==,所以选择用两个string来存储处理后的数据,每次加入字符串中的字符时进行一次比较,有退格就pop,没有就push。
class Solution {
public:
bool backspaceCompare(string s, string t) {
string stS, stT;
for(auto c : s) {
if(c != '#') {
stS.push_back(c);
}
// 必须加上这个判断,否则会出错
else if(! stS.empty()) {
stS.pop_back();
}
}
for(auto c : t) {
if(c != '#') {
stT.push_back(c);
} else if(! stT.empty()) {
stT.pop_back();
}
}
return stS == stT; //直接用两个string的比较作为返回
}
};
2.5 224.基本计算器
题目链接:224. 基本计算器 - 力扣(LeetCode)
分析:用的是括号展开+栈的做法,扫描一遍就可以了,时间复杂度是O(n)。
class Solution {
public:
int calculate(string s) {
stack<int> ops;
ops.push(1);
int i = 0, n = s.length(), ret = 0, sign = 1;
while(i < n) {
if(s[i] == ' ') {
i++;
} else if(s[i] == '+') {
sign = ops.top();
i++;
} else if(s[i] == '-') {
sign = -ops.top();
i++;
} else if(s[i] == '(') {
ops.push(sign);
i++;
} else if(s[i] == ')') {
ops.pop(); //有push就有pop,这是相对的
i++;
} else {
long num = 0;
//获取大于10的数字,保存为num
while(i < n && s[i] >= '0' && s[i] <= '9') {
num = num * 10 + s[i] - '0'; //ascall码,所以要-'0'
i++;
}
ret += num * sign;
}
}
return ret;
}
};
2.6 682.棒球比赛
分析:用一个vector来存储转化后的分数,每次加入的时候进行一次判断,并直接进行总分的计算。因为最后返回的结果是int,输入是string,所以用到了stoi()函数,stoi的参数是const string;是C++的字符处理函数,把数字字符串转换成int输出。
class Solution {
public:
int calPoints(vector<string>& operations) {
int ret = 0; //存储最终的计算结果
vector<int> points; //用数组存储每次的分数
for(auto &ops: operations) {
int n = points.size();
// cout << ops<< endl;
// 必须用ops[0]才可以
switch(ops[0]) {
case '+':
ret += points[n - 1] + points[n - 2];
points.push_back(points[n - 1] + points[n - 2]);
break;
case 'D':
ret += points[n-1] * 2;
points.push_back(points[n-1] * 2);
break;
case 'C':
ret -= points[n-1];
points.pop_back();
break;
default:
//stoi()的参数是const string;是C++的字符处理函数,把数字字符串转换成int输出
ret += stoi(ops);
points.push_back(stoi(ops));
break;
}
}
return ret;
}
};
2.7 496.下一个更大元素 I
题目链接:496. 下一个更大元素 I - 力扣(LeetCode)
分析:用一个unordered_map存储nums2中的greater number,然后遍历nums1就可以得到结果。这样时间复杂度就是 O(nums1.length + nums2.length)。
看到了这篇题解,我觉得总结得非常好:单调栈解决 Next Greater Number 一类问题 - 下一个更大元素 I - 力扣(LeetCode)
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size(), m = nums2.size();
unordered_map<int, int> res;
stack<int> s;
//倒着入栈,也就是正着出栈
for(int i = m - 1; i >= 0; i--) {
while(! s.empty() && s.top() <= nums2[i]) {
s.pop();
}
res[nums2[i]] = s.empty() ? -1 : s.top();
s.push(nums2[i]);
}
//查完nums2中的greater num之后,对nums1进行处理,时间复杂度为 O(nums1.length + nums2.length)
vector<int> res1(n);
for(int i = n - 1; i >= 0; i--) {
res1[i] = res[nums1[i]];
}
return res1;
}
};