目录
Leetcode 20. 有效的括号
Leetcode 1047. 删除字符串中的所有相邻重复项
Leetcode 150. 逆波兰表达式求值
题目链接:Leetcode 20. 有效的括号
题目描述:给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
思路:由于栈结构的特殊性,非常适合做对称匹配类的题目,本题也不例外。首先思考一个问题,什么情况下匹配失败?既然是两两匹配,那么当括号数量为奇数时,一定会有一个括号无法匹配。接下来思考括号数量为偶数时的不匹配情况:
(1)左方向的括号多了
(2)右方向的括号多了
(3)两种括号都不多,但是括号类型不匹配
注:以上图片来源于《代码随想录》
接下来思考这三种情况的代码如何实现?
(1)已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false
(2)遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号,所以return false
(3)遍历字符串匹配的过程中,发现栈顶不是要匹配的字符。所以return false
那么什么时候说明左括号和右括号全都匹配了呢?就是字符串遍历完之后,栈是空的,就说明全都匹配了。
代码如下:
class Solution {
public:
bool isValid(string s) {
//如果是括号数量是奇数,一定不能匹配成功
if (s.size() % 2 != 0)
return false;
stack st;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '(')
st.push(')');
else if (s[i] == '{')
st.push('}');
else if (s[i] == '[')
st.push(']');
// 第二种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,
//说明右括号没有找到对应的左括号 return false
// 第三种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return
// false
else if (st.empty() || st.top() != s[i])
return false;
else
st.pop();
}
// 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,
//说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
return st.empty();
}
};
题目链接:Leetcode 1047. 删除字符串中的所有相邻重复项
题目描述:给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
思路:由于是匹配相邻重复项,因此仍然可以想到用栈的思想。对于本题,既可以新建一个栈,也可以直接在字符串上利用栈的思想解决。
第一种方法:(新建一个栈)
(1)循环遍历字符串,如果栈空或者当前字符与栈顶元素不相等,当前字符入栈;否则将栈顶元素出栈
(2)将栈内字符插入依次到字符串中,由于栈先进后出的性质,此时需要将字符串反转
代码如下:
class Solution {
public:
string removeDuplicates(string s) {
stack st;
//如果当前字符与栈顶元素相同,说明可以消除
for (auto c : s) {
if (st.empty() || c != st.top()) {
st.push(c);
} else {
st.pop();
}
}
string result = "";
while (!st.empty()) {
result += st.top();
st.pop();
}
//由于此时字符串是反的,因此需要反转一次
reverse(result.begin(), result.end());
return result;
}
};
第二种方法:(在字符串上利用栈的思想)
思路和第一种方法类似,只不过不用反转字符串了。
代码如下:
class Solution {
public:
string removeDuplicates(string s) {
string result;
for (auto c : s) {
//字符串为空或者当前字符与字符串最后一个字符不相等,添加到字符串中
if (result.empty() || result.back() != c) {
result.push_back(c);
} else { //否则删去最后一个字符
result.pop_back();
}
}
return result;
}
};
注:由于返回值不计入空间复杂度,因此为O(1)
题目链接:Leetcode 150. 逆波兰表达式求值
题目描述:根据 逆波兰表示法,求表达式的值。有效的运算符包括 + , - , * , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
( 1 + 2 ) * ( 3 + 4 )
。( ( 1 2 + ) ( 3 4 + ) * )
。逆波兰表达式主要有以下两个优点:
1 2 + 3 4 + *
也可以依据次序计算出正确结果。思路:如果了解过二叉树,我们就可以发现:逆波兰表达式相当于是二叉树中的后序遍历。不过本题主要的思想还是栈的思想。这道题和上道题Leetcode 1047. 删除字符串中的所有相邻重复项很像,只不过上道题的思路是:遇到重复元素就消除;本道题的思路是:遇到数字就入栈,遇到运算符就取两次栈顶的元素进行运算。
代码如下:
class Solution {
public:
int evalRPN(vector& tokens) {
//结果比较大,用int过不了部分样例
stack st;
for (int i = 0; i < tokens.size(); i++) {
if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" ||
tokens[i] == "/") {
long long num1 = st.top();
st.pop();
long long num2 = st.top();
st.pop();
if (tokens[i] == "+")
st.push(num2 + num1);
if (tokens[i] == "-")
st.push(num2 - num1);
if (tokens[i] == "*")
st.push(num2 * num1);
if (tokens[i] == "/")
st.push(num2 / num1);
} else { //将字符串转换成数字,并添加到栈中
st.push(stoll(tokens[i]));
}
}
int result = st.top(); //最后剩下的就是最终结果
return result;
}
};
总结:栈的思想应用还是很广泛的,例如递归调用原理、操作系统中进出目录的过程、各种匹配问题的算法题等等。
最后,如果文章有错误,请在评论区或私信指出,让我们共同进步!