题目描述:
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
示例 1:
输入:s = "()" 输出:true
示例 2:
输入:s = "()[]{}" 输出:true
示例 3:
输入:s = "(]" 输出:false
解题思路:
*有很多同学在解题的时候,会在遍历左括号的时候,将左括号放入栈中,遇到右括号再取出,但是这样其实会麻烦很多;其实可以再匹配左括号的时候,将右括号入栈,就只需要比较当前元素和栈顶是否相等就可以了,会比左括号入栈简单很多。
·括号匹配是一个经典的使用栈解决的问题,题目的意思就是,有左括号,相应的也要有右括号
·仔细分析,会有三种不匹配的情况
1.字符串里左括号多余了eg:([{}]()
2.括号没有多余,但是类型不匹配,其实这个和第一个是差不多的,所以可以用同一个表达式解决,但是为了方便同学们理解这道题,所以单独列出。eg:([{}}}
3.字符串里右方向的括号多余了,如果栈空了但是依旧出现右括号说明不匹配eg:([{}])))
·这里还有一个隐藏的点,就是字符串一定得是偶数,如果字符串是奇数就一定不匹配
代码如下:
class Solution {
public:
bool isValid(string s) {
if(s.size() % 2 != 0) return false;//如果s的长度为奇数,直接不匹配
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('}');
//出现栈空没有匹配元素,但是未遍历完成的情况说明不匹配
//出现栈顶元素不匹配
else if(st.empty() || st.top() != s[i]) return false;
else st.pop();
}
return st.empty();//如果最终结果为空说明匹配,不为空则不匹配
}
};
·时间复杂度:O(n)
·空间复杂度:O(n)
总结:
这道题的想法比较巧妙,代码实现难度并不大,技巧性的题目逻辑比较重要。
题目描述:
给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:"abbaca" 输出:"ca" 解释: 例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
解题思路:
·这道题的思路其实和上一题解题思路是一样的,上一题是匹配左右括号,这题是匹配相邻元素,最后再做消除操作
·使用栈来存放,往栈中存放遍历过的元素,当遍历当前的这个元素的时候,去栈中看是否遍历过相同数值的相邻元素,同学们可以直接在纸上进行操作
·因为从栈中取出的元素是倒序的,最后需要再增加一步反转字符串
代码如下:
class Solution {
public:
string removeDuplicates(string S) {
stack st;
for(char s:S){
if(st.empty() || s != st.top()){
st.push(s);
}else{
st.pop();//如果s和栈顶元素一致,则移除
}
}
string result = "";
while(!st.empty()){//将栈中元素放到result字符串中
result += st.top();
st.pop();
}
reverse(result.begin(),result.end());因为从栈中取出是倒序,所有需要反转
return result;
}
};
·时间复杂度:O(n)
·空间复杂度:O(n)
番外
这道题也可以不使用栈解题,可以直接对字符串进行操作
代码如下:
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;
}
};
·时间复杂度:O(n)
·空间复杂度:O(1)
总结
这道题比上一题还简单一些,可以直接想到,没有那么多种情况,依旧是需要多次练习
LeetCode150.逆波兰表达式求值
题目描述:
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
'+'
、'-'
、'*'
和 '/'
。示例 1:
输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = ["4","13","5","/","+"] 输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"] 输出:22 解释:该算式转化为常见的中缀算术表达式为: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22
解题思路:
·其实逆波兰表达式相当于二叉树中的后序遍历。可以把运算符作为中间节点,按照后序遍历的规则画出一个二叉树,但是没必要从二叉树的角度去解决,只需要知道逆波兰表达式是用后序遍历的方式把二叉树序列化了。
·本题中每一个子表达式要得出一个结果,再用这个结果进行运算,那么就是和上一题,相邻字符串消除的过程很像了吗(运算字符前两个数字进行相对应的运算,再存入栈中)
代码如下:
class Solution {
public:
int evalRPN(vector& tokens) {
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();
st.pop();
return result;
}
};
·时间复杂度:O(n)
·空间复杂度:O(n)
总结:今天的题目都是技巧题,代码理解难度都不高,但是都需要反复练习,并且要将其融会贯通,可以使用在别的情形中。