今天的题目都是对栈的经典应用。
有效的括号
题目链接:力扣
解题思路:基于栈结构的特殊性,其非常适合做对称匹配类问题,其实如果知道了要用栈解这道题,在脑中模拟一遍,接下来的思路就是自然而然能够想到的
这题这里还包含了一个剪枝的条件:若括号都是匹配的,则该字符串定为偶数,若字符串长度为奇数,则一定不匹配。
下面附上我的题解:
class Solution {
public:
bool isValid(string s) {
if(s.size() % 2 != 0) //剪枝
return false;
stack<char> Mystack;
for(int i=0 ; i<s.size(); i++)
{
if(s[i] == '(' || s[i] == '{' || s[i] == '[') //此时执行入栈操作
Mystack.push(s[i]);
else //此时执行出栈操作
{
if(Mystack.empty()) return false; //若栈为空,则直接退出
if((Mystack.top() == '(' && s[i] == ')')
||(Mystack.top() == '[' && s[i] == ']')
||(Mystack.top() == '{' && s[i] == '}') ) //此时看栈顶元素和该元素是否是一对
Mystack.pop(); //一致,执行pop
else //不一致,则退出
return false;
}
}
return Mystack.empty();
}
};
卡哥的给出的答案运用了一点小技巧, 他在匹配左括号的时候,用右括号入栈,就只需要比较当前元素和栈顶相不相等就可以了,这样写的代码更加简洁。 如下所示:
class Solution {
public:
bool isValid(string s) {
if (s.size() % 2 != 0) return false; // 如果s的长度为奇数,一定不符合要求
stack<char> 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(); // st.top() 与 s[i]相等,栈弹出元素
}
// 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
return st.empty();
}
};
删除字符串中的所有相邻重复项
题目链接:力扣
解题思路:其实这题和上题很像,上题的实质是删除相邻括号,即匹配左右括号。这题是删除相邻的重复字符,像这种消除相邻匹配的题,用栈就很合适。
这题也是如果知道用栈来辅助解题,则思路是自然而然的
若栈顶不等于要加入的元素,则加入元素;
若栈顶等于要加入的元素,则将栈顶pop掉 ;
最后栈中留下了的元素便可以组成最终不带相邻重复项的字符串
贴上我的代码:
class Solution {
public:
string removeDuplicates(string s) {
stack<char> Mystack;
string res="";
for(int i = 0; i<s.size();i++)
{
if(Mystack.empty() || Mystack.top() != s[i] )
Mystack.push(s[i]);
else
Mystack.pop();
}
int size = Mystack.size();
for(int i=0;i<size;i++)
{
res = Mystack.top() + res;
Mystack.pop();
}
return res;
}
};
卡哥的想法大体上无差,但是他直接利用string来代替stack,这样可以省去栈转为字符串的操作。
class Solution {
public:
string removeDuplicates(string S) {
string result;
for(char s : S) {
if(result.empty() || result.back() != s) {
result.push_back(s);
}
else {
result.pop_back();
}
}
return result;
}
};
逆波兰表达式求值
题目链接:力扣
解题思路 :刚拿到题目的时候有点懵,看不懂题目在说啥,直到看到leetcode上有下述文字,才恍然大悟。
其实最后一句话已经点名了本题的解题思路:
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
如此以来,代码很好想了
下面贴上我的代码(卡哥的想法与我的类似,就不贴了。
但是在卡哥的代码中,string转long long int 用到了一个C++11的字符串函数stoll()
即stoll(tokens[i]),我第一次见到这个函数,在此记录一下):
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> Mystack;
for(int i=0 ; i<tokens.size() ; i++)
{
if (tokens[i] != "+" && tokens[i] != "-" && //若是数字则压栈
tokens[i] != "*" && tokens[i] != "/")
{
int temp = atoi(tokens[i].c_str());
Mystack.push(temp);
}
else //如果是运算符,则取栈中前两个值,进行运算
{
int num1 = Mystack.top();
Mystack.pop();
int num2 = Mystack.top();
Mystack.pop();
int res = 0;
if(tokens[i] == "+")
res = num2 + num1;
else if(tokens[i] == "-")
res = num2 - num1;
else if(tokens[i] == "*")
res = num2 * num1;
else if(tokens[i] == "/")
res = num2 / num1;
Mystack.push(res); //将答案压回栈中
}
}
return Mystack.top();
}
};
ps:虽然我们习惯采用中缀表达式(4 + 13 / 5)但计算机采用中缀表达式时不但要比较优先级,还需要做回退操作。
后缀表达式(["4", "13", "5", "/", "+"]) 可以使计算机用栈来顺序处理,不用考虑计算的优先级,这对计算机是非常友好的。这道题展现了计算机的思考方式。