代码随想录训练营打卡DAY 11 | 栈的应用

代码随想录训练营打卡DAY 11 | 栈的应用

有效的括号

20.有效的括号(leetcode链接)

题目描述

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 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)

逆波兰表达式求值

150.逆波兰表达式求值(leetcode链接)

题目描述

给你一个字符串数组 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)

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值