20. 有效的括号
题目链接/文章讲解/视频讲解:代码随想录
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
思路:
对栈有了一些理解后,看了Carl的思路讲解,然后独立AC了本道题。主要思路就是注意三点。一、字符串左括号是否多余?二、字符串括号顺序是否正确?三、字符串右括号是否多余?当我们遇见左括号时,我们先按顺序将左括号对应的右括号存入栈中,如果遇见对应的右括号,就从栈里面删除,直到最后栈为空则字符串有效。
情况一:
情况二:
情况三:
代码
class Solution {
public:
bool isValid(string s) {
stack<int> stackin;
for(int i = 0; i < s.size(); i++){
if(s[i] == '(') {
stackin.push(')');
continue;
}
else if(s[i] == '{'){
stackin.push('}');
continue;
}
else if(s[i] == '['){
stackin.push(']');
continue;
}
else if(!stackin.empty() && s[i] == stackin.top()){
stackin.pop();
continue;
}
else{
return false;
}
}
return stackin.empty() ? true : false;
}
};
理解这种思路, 左右对应,左添右删。
1047. 删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
- 输入:"abbaca"
- 输出:"ca"
- 解释:例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
思路:
这道题就像是开心消消乐,当我消除了两个一样的字符之后,我们需要考虑这两个字符串两侧的字符串是否也需要消除?从代码随想录理解到:栈是用来存取我们已经遍历过的元素,有效的括号那道题也是这样,当我们遍历到右括号时,我们需要去检查之前是否遇见过左括号。 简单AC。
代码实现 :
class Solution {
public:
string removeDuplicates(string s) {
stack<char> stackin;
stack<char> stackout;
for(int i = 0; i < s.size(); i++){
if(stackin.empty() || s[i] != stackin.top()){
stackin.push(s[i]);
}
else{
stackin.pop();
}
}
while(!stackin.empty()){
stackout.push(stackin.top());
stackin.pop();
}
string res;
while(!stackout.empty()){
res.push_back(stackout.top());
stackout.pop();
}
return res;
}
};
想着联动一下前面学习的用栈实现队列, 其实这里如果使用队列会更简单一点,也可以只使用一个栈,然后使用reverse()函数来反转一下输出即可。
150. 逆波兰表达式求值
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。 - 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
思路:
理解逆波兰表达式的求值思路,即取当前两个数存入栈,遇见符号就拿出进行计算,然后将计算结果压入栈。最后栈中所得值即为结果。
主要是一些特殊的小细节需要去认真学习,比如:从字符串容器中读出的数字如何压入到整型的栈中?得到的结果需不需要担心溢出问题?
代码:
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> stackin;
long long temp1, temp2;
for(int i = 0; i < tokens.size(); i++){
if(tokens[i] == "+"){
temp1 = (long long) stackin.top();
stackin.pop();
temp2 = (long long) stackin.top();
stackin.pop();
stackin.push(temp2 + temp1);
}
else if(tokens[i] == "-"){
temp1 = (long long) stackin.top();
stackin.pop();
temp2 = (long long) stackin.top();
stackin.pop();
stackin.push(temp2 - temp1);
}
else if (tokens[i] == "*"){
temp1 = (long long) stackin.top();
stackin.pop();
temp2 = (long long) stackin.top();
stackin.pop();
stackin.push(temp2 * temp1);
}
else if (tokens[i] == "/"){
temp1 = (long long) stackin.top();
stackin.pop();
temp2 = (long long) stackin.top();
stackin.pop();
stackin.push(temp2 / temp1);
}
else{
stackin.push(stoll(tokens[i]));
}
}
return (long long) stackin.top();
}
};
这里可以考虑将数字插入栈放在if处,运算过程放在else处,可以省去一些代码:
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<long long> stackin;
long long temp1, temp2;
for(int i = 0; i < tokens.size(); i++){
if(tokens[i] != "+" && tokens[i] != "-" && tokens[i] != "*" && tokens[i] != "/"){
stackin.push(atoi(tokens[i].c_str()));
}
else{
temp1 = (long long) stackin.top();
stackin.pop();
temp2 = (long long) stackin.top();
stackin.pop();
if(tokens[i] == "+"){
stackin.push(temp2 + temp1);
}
else if(tokens[i] == "-"){
stackin.push(temp2 - temp1);
}
else if (tokens[i] == "*"){
stackin.push(temp2 * temp1);
}
else if (tokens[i] == "/"){
stackin.push(temp2 / temp1);
}
}
}
return (long long) stackin.top();
}
};
总结:
今天的三道题算是比较简单的,理解了栈的后进先出思想后,熟练使用pop(),top(),push()函数,就能够比较好的解决一些栈相关的问题。尤其是需要检查先前遍历的元素问题可以用栈来进行解决。