【算法训练 day11 有效的括号、删除字符串中的所有相邻重复项、逆波兰表达式求值】

本文介绍了如何使用栈实现LeetCode中的三个问题:有效括号的验证、删除字符串中的相邻重复项以及逆波兰表达式的求值。通过栈的操作,解决这些涉及字符串处理和算术运算的问题,展示了算法应用和逻辑设计的关键点。
摘要由CSDN通过智能技术生成


一、有效的括号-LeetCode 20

Leecode链接: leetcode 20
文章链接: 代码随想录
视频链接: B站

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

示例:

输入:s = "()"
输出:true

输入:s = "(]"
输出:false

输入:s = "()[]{}"
输出:true

思路

使用堆栈,遇到左括号就插入它自己,遇到右括号就弹出栈顶元素对比,如果有一个不匹配就返回false,直到遍历结果且栈中没有元素时才能返回true,如果栈中有剩余元素表示整体括号数量是一个奇数,也就是不匹配。

实现代码

//cpp
class Solution {
public:
    bool isValid(string s) {
        int len = s.size();
        if(len%2 == 1) return false;
        stack<char> st;

        for(int i = 0;i<len;i++){

            if(s[i] == '('|| s[i] == '{' || s[i] == '['){
                st.push(s[i]);
            }
            else{
                if(st.empty()){
                    return false;
                }

                char temp = st.top();
                st.pop();
                if(temp == '[' && s[i] != ']'){
                    return false;
                }
                else if(temp == '{' && s[i] != '}'){
                    return false;
                }
                else if(temp == '(' && s[i] != ')'){
                    return false;
                }
            }
        }

        if(!st.empty()){
            return false;
        }

        return true;
    }
};
//cpp
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();
    }
};

个人问题

我在这直接插入字符本身,当然卡哥那种插入对应的右括号然后直接对比是否相等也可以,需要注意的是,我这里有个想法与卡哥不一致,每道题例子数量有限,每个人不可能都想得到十全十美的代码解法,leecode上有很多你想不到的测试案例,只要能根据结果找到错误所在,并给出解决方案,这就是能力,谁都不可能一次ac所有题目代码,临场救急也是一种能力,所以不要有心理负担。

总结

整体比较简单,需要注意各个陷阱案例,在if分支中尽量考虑到可能的情况。算法时间复杂度为O(n),空间复杂度为O(n)。


二、删除字符串中的所有相邻重复项-LeetCode 1047

Leecode链接: LeetCode 1047
文章链接: 代码随想录
视频链接: B站

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:

输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"

思路

一边遍历字符串,一边把字符压栈,压栈规则如下:如果栈为空,直接压栈;如果不为空且栈顶元素与当前遍历元素不同,压栈;以上两个条件都不满足,则将栈顶元素弹出。但也可以不用栈,直接一边遍历一边插入,插入前对比是否相同再决定是否使用尾删法。

实现代码

//cpp
class Solution {
public:
    string removeDuplicates(string s) {
        stack<char> st;
        string res = "";
        for(char c : s){
            if(st.empty()){
                st.push(c);
            }
            else{
                if(st.top() == c){
                    st.pop();
                }
                else{
                    st.push(c);
                }
            }
        }

        while(!st.empty()){
            res += st.top();
            st.pop();
        }
        reverse(res.begin(),res.end());

        return res;
    }
};
//cpp
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;
    }
};

个人问题

在实现代码时,for循环那里我最初使用的形式为:for(int i = 0;i<s.size();i++),单个结果通过,但提交时出现内存超出限制,使用c++11标准后没有报错。

总结

题目需要删除的是相邻元素,类似于括号那题,需要配成一对的去考虑都可以用栈的思想,时间复杂度为O(n),空间复杂度为O(n),不使用栈情况下:时间复杂度为O(n),空间复杂度为O(1)。


三.逆波兰表达式求值-LeeCode 150

Leecode链接: LeetCode 150
文章链接: 代码随想录
视频链接: B站

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:
有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用 32 位 整数表示。

示例:

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

思路

与有效括号那题几乎一样,遍历字符串数组,遇到数字字符串就压栈,遇到符号就出栈两个元素,将计算好的结果再压栈进去,直到遍历结束,返回的栈顶元素一定是运算结果。

实现代码

//cpp
class Solution {
public:
    long long strtonum(string s) {
        bool isNegative = false;
        long long sum = 0;
        int i = 0;
        if (s[0] == '-') {
            isNegative = true;
            i++;
        }

        for (i; i < s.size(); i++) {
            if (s[i] >= '0' && s[i] <= '9') {
                sum = sum * 10 + (s[i] - '0');
            }
        }

        if (isNegative) {
            return -sum;
        }
        else {
            return sum;
        }
    }

    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] != "/") {
                st.push(strtonum(tokens[i]));
            }
            else {
                long long temp1 = st.top();
                st.pop();
                long long temp2 = st.top();
                st.pop();
                if (tokens[i] == "+") {
                    st.push(temp1 + temp2);
                }
                else if (tokens[i] == "-") {
                    st.push(temp2 - temp1);
                }
                else if (tokens[i] == "*") {
                    st.push(temp1 * temp2);
                }
                else {
                    st.push(temp2 / temp1);
                }
                
            }
        }
        long long res = st.top();
        return res;
    }
};

个人问题

个人编写的代码在判断是否是符号那里出了问题,使用逻辑或 || 符号,导致遇到运算符时也压入栈。正确逻辑应该是只有当都不是运算符时才能被判定为数字,需要压栈处理。我自己把字符串转数字的函数也实现了一遍,实现时需要考虑负数问题。当然也可以直接用string头文件包含的库函数。

总结

考验对栈的应用。时间复杂度O(n),空间复杂度O(n)。

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值