一、LeetCode 20.有效的括号
状态:已解决
1.思路
括号匹配题就跟玩消消乐一样,一旦出现右括号,必定要去前面寻找相应的左括号。我们在栈中只保存左括号,而遇到一个右括号就在栈中寻找它对应的左括号,此时若是有效的括号对,那么它相应的左括号必定在栈顶,因为要是栈顶不是它相应的左括号,根据栈的入栈顺序,必然有其他类别的左括号在其之后,那么配套的右括号也应该在这个右括号的前面,但右括号一旦遇到就必定要消去对应的左括号,不存在栈中还有剩余的情况,因此矛盾。故若是有效的括号对,那么某个右括号相应的左括号必定在栈顶。
因此,此题括号不匹配有三种情况:
(1)遇到右括号,但栈顶元素不是左括号。
(2)左括号多了,最后遍历完字符串栈未空。
(3)右括号多了,最后字符串未遍历完,但栈空了。
只有字符串遍历完并且栈空了才是完全有效的括号集合。根据以上理论构造代码。
2.代码实现
(1)我的代码:关于存储左右括号配对的问题,我是用了一个map结构存放的,key放有括号的,value放左括号。这里有个剪枝,假如字符串长度为奇数怎么都不可以完全匹配。
class Solution {
public:
bool isValid(string s) {
if(s.size() % 2 != 0) return false;
unordered_map<char,char> brace;
brace.emplace(')','(');
brace.emplace('}','{');
brace.emplace(']','[');
stack<char> z;
int i = 0;
while(i < s.size()){
auto iter = brace.find(s[i]);
if(iter != brace.end()){
if(z.empty() || iter->second != z.top()){
return false;
}else if(iter->second == z.top()){
z.pop();
}
}else{
z.push(s[i]);
}
i++;
}
if(z.empty()) return true;
else return false;
}
};
(2)讲解代码:直接判断,如果是左括号就放配对的右括号,到时候直接和右括号消消看就可。
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();
}
};
时间复杂度:O(n)
空间复杂度:O(n)
二、LeetCode 1047. 删除字符串中的所有相邻重复项
状态:已解决
1.思路
跟上道题几乎差不多的思路,甚至更简单,因为上道题是匹配,还要想办法保存配对,但这道题是消除相同元素,因此无需保存配对,直接判等。只是最后要顺序输出剩余字符串,故需要翻转一下。
2.代码实现
class Solution {
public:
string removeDuplicates(string s) {
stact<char> in;
string result;
for(int i=0;i<s.size();i++){
if(!in.empty() && in.top() == s[i]){
in.pop();
}else{
in.push(s[i]);
}
}
while(!in.empty){
result += in.top();
in.pop();
}
reverse(in.begin(),in.end());
return result;
}
};
居然和老师差不多,开心。
时间复杂度: O(n)
空间复杂度: O(n)
三、LeetCode 150. 逆波兰表达式求值
题目链接/文章讲解/视频讲解: https://programmercarl.com/0150.%E9%80%86%E6%B3%A2%E5%85%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B1%82%E5%80%BC.html状态:已解决
1.思路
这道题其实没有很大的难度,因为题中已经转换成了逆波兰表示法表示的算术表达式,只需要理解逆波兰表达式是什么并且何时出栈入栈即可。其实逆波兰表达式相当于是二叉树中的后序遍历。 大家可以把运算符作为中间节点,按照后序遍历的规则画出一个二叉树。但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后序遍历的方式把二叉树序列化了,就可以了。
其实,本题的实质是每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么这就是一个相邻字符串消除的过程,本质还是消消乐。
这题入栈是遇到数字,出栈是遇到运算符,然后将栈顶两个元素拿出来做运算后再将结果入栈。
2.代码实现
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 nums1 = st.top();st.pop();
long long nums2 = st.top();st.pop();
if(tokens[i] == "+") st.push(nums2+nums1);
if(tokens[i] == "/") st.push(nums2/nums1);
if(tokens[i] == "-") st.push(nums2-nums1);
if(tokens[i] == "*") st.push(nums2*nums1);
}else{
st.push(stoll(tokens[i]));
}
}
int result = st.top();
st.pop();
return result;
}
};
时间复杂度: O(n)
空间复杂度: O(n)
栈主要是用来保存之前遍历过的元素的,适合运用在不断与之前元素做配对或者消除的题。