代码随想录训练营打卡DAY 11 | 栈的应用
有效的括号
题目描述
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = “()”
输出:true
示例 2:
输入:s = “()[]{}”
输出:true
示例 3:
输入:s = “(]”
输出:false
提示:
- 1 <= s.length <= 104
- s 仅由括号 ‘()[]{}’ 组成
题解
思路一:卡哥在讲解本题的时候说搞清楚3种不匹配的状态即可,但一般人是很难自己归纳出只有3种不匹配的情况的。笔者认为从处理每个字符出发即可:
- 左括号直接压入
- 右括号需要检查top
- 最终若是空栈,则返回true,反之返回false
核心代码【C++】:
// 写法一,将左括号压入栈,这种容易想到,但是处理没有将右括号压入栈来得方便
bool isValid(string s) {
std::stack<char> mystack;
for(int i = 0;i < s.length(); i++){
switch (s[i])
{
case '(':
case '{':
case '[':
mystack.push(s[i]);
break;
case ')':
if(mystack.empty())return false;
else if(mystack.top() == '('){
mystack.pop();
break;
}
else return false;
case ']':
if(mystack.empty())return false;
else if(mystack.top() == '['){
mystack.pop();
break;
}
else return false;
case '}':
if(mystack.empty())return false;
else if(mystack.top() == '{'){
mystack.pop();
break;
}
else return false;
default:
break;
}
}
if(mystack.empty())return true;
else return false;
}
// 写法二:将右括号压入栈种
bool isValid(string s){
stack<char> stk;
for(char c : s){
switch (c)
{
// 遍历到左括号,往栈里push右括号(因为右括号出栈时好比较
// 这个写法可以与第一个写法比较,第一个写法push左括号,后面逻辑就不好写了
case '(':
stk.push(')');
break;
case '[':
stk.push(']');
break;
case '{':
stk.push('}');
break;
case ')':
case ']':
case '}':
if(stk.empty()) return false;// 由于C++空栈中top是未定义行为,所以需要先empty再top
else if(stk.top() == c) stk.pop();
else return false;
default:
break;
}
}
if(stk.empty()) return true;
else return false;
}
思路二:这里利用了键值对,想法很巧妙,但是也有点奇怪。
bool isValid(string s){
int n = s.size();
if(n%2 == 1){ // 细节!奇数时一定不匹配
return false;
}
stack<char> stk;
// 键值对,先键后值
unordered_map<char,char> pairs = {
{')','('},{'}','{'},{']','['}
};
for(char ch:s){
// find找不到时返回尾迭代器
if(pairs.find(ch) != pairs.end()){
if(stk.empty() || stk.top() != pairs[ch]){
return false;
}
else{
stk.pop();
}
}
else{
stk.push(ch);
}
}
return stk.empty();
}
以上3段代码时间和空间复杂度均为: O ( n ) O(n) O(n)
删除字符串中的所有相邻重复项
1047.删除字符串中的所有相邻重复项(leetcode链接)
题目描述
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例:
输入:“abbaca”
输出:“ca”
解释:例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。
提示:
- 1 <= S.length <= 20000
- S 仅由小写英文字母组成。
题解
思路:本题只要注意到使用栈这种数据结构即可。
核心代码【C++】:
string removeDuplicates(string s) {
// 利用字符串模拟栈,最后就不用重排顺序(因为用出栈是倒序
string newString;
for(char c : s){
if(newString.empty()) newString.push_back(c);
else if(newString.back() == c) newString.pop_back();
else newString.push_back(c);
}
return newString;
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)
逆波兰表达式求值
题目描述
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。(后来leetcode更新数据需使用long long
示例 1:
输入:tokens = [“2”,“1”,“+”,“3”,“*”]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = [“4”,“13”,“5”,“/”,“+”]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
题解
思路:逆波兰表达式有一个特点,若忽略数字,则从左到右遍历的运算符顺序即计算顺序,故处理如下:
- 遍历到数字:直接压入栈中
- 遍历到符号:弹出栈顶2个数并计算,注意左右操作数顺序
题外话:从中缀表达式转化为后缀表达式也需要栈,一般称为运算符栈。而本题用到的栈称运算数栈。
核心代码【C++】:
int evalRPN(vector<string>& tokens) {
std::stack<int> stk;
int left,right;
for(string ch:tokens){
if(ch=="+" || ch=="-" || ch=="*" || ch=="/"){
right = stk.top();
stk.pop();
left = stk.top();
stk.pop();
switch (ch[0])
{
case '+':
stk.push(left+right);
break;
case '-':
stk.push(left-right);
break;
case '*':
stk.push(left*right);
break;
case '/':
stk.push(left/right);
break;
default:
break;
}
}
else{
stk.push(stoi(ch));
}
}
return stk.top();
}
// 错误示范,很不简洁而且需要特殊处理负数
int evalRPN(vector<string>& tokens) {
stack<long long> nums;
long long temp;
for(string c : tokens){
switch (c[0])
{
case '+':
temp = nums.top();
nums.pop();
temp = nums.top() + temp;
nums.pop();
nums.push(temp);
break;
case '-'://这里有可能是一个整数的负号,第一种写法好就规避了这个问题而且更简洁
if(c.size() == 1){
temp = nums.top();
nums.pop();
temp = nums.top() - temp;
nums.pop();
nums.push(temp);
}
else{
nums.push(stoll(c));
}
break;
case '*':
temp = nums.top();
nums.pop();
temp = nums.top() * temp;
nums.pop();
nums.push(temp);
break;
case '/':
temp = nums.top();
nums.pop();
temp = nums.top() / temp;
nums.pop();
nums.push(temp);
break;
default:
nums.push(stoll(c));
break;
}
}
return nums.top();
}
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
n
)
O(n)
O(n)