文档讲解:代码随想录 (programmercarl.com)
视频讲解:栈的拿手好戏!| LeetCode:20. 有效的括号_哔哩哔哩_bilibili
状态:没做出来
其实这个题目的描述是很模糊的,这就需要去仔细地想各种不匹配的情况,并且总结出规律,对刚刚接触这个题目的人来说并不是那么友好。
看了视频之后,总结出其实不匹配的情况就三种,用语言总结其实就两种:1. 左右括号数量不匹配 2.相邻括号类型不匹配。
但是,左右括号数量不匹配其实是两种情况 ,因为在用栈来解决这类符号匹配问题的时候,如果左边括号(例如“{”)数量比右边(例如“}”)的多,那就会出现明明已经遍历完了整个字符串,但是栈内还是有元素,如果右边括号数量比左边的多,就会出现正准备匹配的时候,发现栈空了,没有元素给右边的括号匹配。
而相邻括号类型不匹配,就是在匹配的时候发现栈顶的元素类型(这里指的是“}”“)”“]”三种类型),和自身的类型对应不上。
class Solution {
public:
bool isValid(string s) {
stack<char> sta;
for(int i = 0; i < s.size(); i++){
if(s.size() % 2 != 0)return false;
else if(s[i] == '(')sta.push(')');
else if(s[i] == '{')sta.push('}');
else if(s[i] == '[')sta.push(']');
else if(sta.empty() || s[i] != sta.top()){
return false;
}
else{
sta.pop();
}
}
if(!sta.empty())return false;
return true;
}
};
这里说一下为什么push的是相反方向的括号,这是为了让匹配的时候可以直接匹配不用考虑条件,即不用考虑左括号怎么映射到对应的右括号。
文档讲解:代码随想录 (programmercarl.com)
视频讲解:栈的好戏还要继续!| LeetCode:1047. 删除字符串中的所有相邻重复项_哔哩哔哩_bilibili
状态:AC
因为只用匹配相邻的字符,所以想到用栈更加方便。
每一位push到栈里之前,要判断一下当前位置元素是否和栈顶元素相等,因为栈是后进先出的,所以栈顶元素就是字符串当前元素的上一个元素,这就是在判断相邻元素是否相等。
当然,判断相等之前要判断是否栈空,不然就涉及访问空栈的异常。
遍历完之后,栈内的元素就是处理好了之后字符串的所有元素,因为我已经记录下了所有的元素,所以可以直接给字符串缩容,并且从字符串末尾开始赋值,因为栈是后进先出的。看题解的时候发现代码中新建了一个字符串来存栈内元素,感觉有点浪费空间了。
class Solution {
public:
string removeDuplicates(string s) {
stack<char> st;
for(int i = 0; i < s.size();++i){
if(st.empty() || s[i] != st.top()){
st.push(s[i]);
}
else if(s[i] == st.top()){
st.pop();
}
}
s.resize(st.size());
for(int i = s.size()-1; i >= 0 ; --i){
s[i] = st.top();
st.pop();
}
return s;
}
};
文档讲解:代码随想录 (programmercarl.com)
视频讲解:栈的最后表演! | LeetCode:150. 逆波兰表达式求值_哔哩哔哩_bilibili
状态:没做出来
看完题目,其实已经交代得很清楚了,什么是逆波兰表达式?简单理解一下就是平时加减乘除的数学表达式,都是树的中序遍历,现在变成了树的后序遍历而已。
而且题目还提到
- 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
思路已经明确了,然后就是写代码,写代码过程中我认为比较棘手的三个点:1. 怎么判断遍历的当前元素是数字还是字符?其实这个很简单,字符就加减乘除四种情况,在if语句中判断一下,剩下的就都是数字了。当时我是先判断的数字,所以没那么顺。2. 怎么把字符串转变为整型?C++标准库提供了相应的函数stoi()(将字符串转成int类型),stoll(将字符串转成long long类型)。3. 每次运算之后的结果存到哪里?其实可以继续存到栈里,仔细想想这个操作并不影响后面的入栈出栈,并且还让代码标准化了,省去了多余的代码。
一开始用int发现总是越界,最后看题解说是改了用例,所以要把所有int类型全部改成long long 类型。另外要注意push的时候为什么是tmp2在前面,因为栈是后进先出的,所以tmp1存的才是运算符后面的操作数,加法乘法没关系,主要是减法和除法,减数和除数不一样会影响结果。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> st;
for(int i = 0; i <tokens.size();i++){
if(tokens[i] == "+" ||tokens[i] == "-" ||tokens[i] == "*" ||tokens[i] == "/" ){
long long tmp1 = st.top();
st.pop();
long long tmp2 = st.top();
st.pop();
if(tokens[i] == "+")st.push(tmp2 + tmp1);
if(tokens[i] == "-")st.push(tmp2 - tmp1);
if(tokens[i] == "*")st.push(tmp2 * tmp1);
if(tokens[i] == "/")st.push(tmp2 / tmp1);
}else{
st.push(stoll(tokens[i]));
}
}
long long result = st.top();
st.pop();
return result;
}
};
三道题目写下来,发现栈的逻辑并不难,难的是识别什么时候要运用到栈。