算法第六天(基于某某随想录)

第五章 栈与队列 part02

今日内容:

  • 20.有效的括号
  • 1047.删除字符串中的所有相邻重复项
  • 150.逆波兰表达式求值

详细布置

20.有效的括号

讲完了栈实现队列,队列实现栈,接下来就是栈的经典应用了。

大家先自己思考一下 有哪些不匹配的场景,在看视频 我讲的都有哪些场景,落实到代码其实就容易很多了。

题目链接/文章讲解/视频讲解:https://programmercarl.com/0020.%E6%9C%89%E6%95%88%E7%9A%84%E6%8B%AC%E5%8F%B7.html

c++:

思路:从前往后开始模拟,如果是左括号的话,加入栈中,如果是右括号的话,则与当前的栈
看下是否匹配(根据栈的特性先进后出),所以当前栈里的元素一定是距离当前右括号最近的
那个左括号,然后依次匹配即可

class Solution {
public:
    bool isValid(string s) {
        stack<int> stk;
        for(auto c:s){
            if(c == '(' || c == '{' || c == '[')stk.push(c);
            else {
                if(stk.empty())return false;
                if(c == ')' && stk.top() == '(') stk.pop();
                else  if(c == '}' && stk.top() == '{') stk.pop();
                else  if(c == ']' && stk.top() == '[') stk.pop();
                else return false;
            }
        }
        return stk.empty();
    }
};

java:

解题思路:
  • 算法原理
    • 栈先入后出特点恰好与本题括号排序特点一致,即若遇到左括号入栈,遇到右括号时将对应栈顶左括号出栈,则遍历完所有括号后 stack 仍然为空;
    • 建立哈希表 dic 构建左右括号对应关系:key 左括号,value 右括号;这样查询 2个括号是否对应只需 O(1) 时间复杂度;建立栈 stack,遍历字符串 s 并按照算法流程一一判断。
  • 算法流程
    1. 如果 c 是左括号,则入栈 push;
    2. 否则通过哈希表判断括号对应关系,若 stack 栈顶出栈括号 stack.pop() 与当前遍历括号 c 不对应,则提前返回 false。
  • 提前返回 false
    • 提前返回优点: 在迭代过程中,提前发现不符合的括号并且返回,提升算法效率。
    • 解决边界问题:
      • stack 为空: 此时 stack.pop() 操作会报错;因此,我们采用一个取巧方法,给 stack 赋初值 ?,并在哈希表 dic 中建立 key:′?′,value:′?′的对应关系予以配合。此时当 stack 为空且 c 为右括号时,可以正常提前返回 false;
      • 字符串 s 以左括号结尾: 此情况下可以正常遍历完整个 s,但 stack 中遗留未出栈的左括号;因此,最后需返回 len(stack) == 1,以判断是否是有效的括号组合。
  • 复杂度分析
    • 时间复杂度 O(N):正确的括号组合需要遍历 1遍 s
    • 空间复杂度 O(N):哈希表和栈使用线性的空间大小。
class Solution {
    private static final Map<Character,Character> map = new HashMap<Character,Character>(){
        {
            put('{','}');
            put('[',']');
            put('(',')');
            put('?', '?');
        }
    };

    public boolean isValid(String s) {
        if(s.length() > 0  && !map.containsKey(s.charAt(0))) return false;
        LinkedList<Character> stack = new LinkedList<Character>() { {add('?');}};
        for(Character c : s.toCharArray()){
            if(map.containsKey(c)) stack.add(c);
            else if(map.get(stack.removeLast()) != c || stack.isEmpty()) return false;
        }
        return stack.size() == 1;
    }
}

1047.删除字符串中的所有相邻重复项

栈的经典应用。

要知道栈为什么适合做这种类似于爱消除的操作,因为栈帮助我们记录了 遍历数组当前元素时候,前一个元素是什么。

题目链接/文章讲解/视频讲解:https://programmercarl.com/1047.%E5%88%A0%E9%99%A4%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E7%9A%84%E6%89%80%E6%9C%89%E7%9B%B8%E9%82%BB%E9%87%8D%E5%A4%8D%E9%A1%B9.html

c++:

(栈) O(n)
  1. 开一个栈,然后扫描整个字符串。
  2. 如果当前字符和栈顶元素不相等,则当前字符入栈;如果相等,则当前栈顶元素出栈。
  3. 最后将栈中元素全部出栈,按正序拼成答案即可。
时间复杂度
  • 由于每个字符最多被扫描一次,故时间复杂度为 O(n)。
空间复杂度
  • 需要额外的栈空间,以及答案还需要空间存储,故总空间复杂度为 O(n)。

    本题拿字符串直接作为栈,这样省去了栈还要转为字符串的操作。

class Solution {
public:
    string removeDuplicates(string s) {
        string res;
        for(char c : s)
        {
            if(res.size() > 0 && c == res.back()) res.pop_back();
            else res += c;
        }
        return res;

    }
};

java:

class Solution {
    public String removeDuplicates(String s) {
        char[] cs = s.toCharArray();
        Deque<Character> d = new ArrayDeque<>();
        for (char c : cs) {
            if (!d.isEmpty() && d.peekLast().equals(c)) {
                d.pollLast();
            } else {
                d.addLast(c);
            }
        }
        StringBuilder sb = new StringBuilder();
        while (!d.isEmpty()) sb.append(d.pollLast());
        sb.reverse();
        return sb.toString();
    }
}

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

c++:

(栈操作) O(n)

遍历所有元素。如果当前元素是整数,则压入栈;如果是运算符,则将栈顶两个元素弹出做相应运算,再将结果入栈。
最终表达式扫描完后,栈里的数就是结果。

时间复杂度分析:每个元素仅被遍历一次,且每次遍历时仅涉及常数次操作,所以时间复杂度是 O(n)。

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<long long> st;
        for(auto c: tokens){
            if(c == "+" || c == "-" || c == "*" || c == "/"){
                long long b = st.top(); st.pop();    // 后面一个是第二个操作数 (要注意哦)
                long long a = st.top(); st.pop();
                long long x;
                if(c == "+") x = a + b;
                else if(c == "-") x = a - b;
                else if(c == "*") x = a * b;
                else x = a / b;
                st.push(x);
            }else st.push(stoi(c));         // 是一个数的话直接 st.push(c)
        }      // stoi(c) 把字符串类型的数字变成, int 类型的数字

        return st.top();
    }
};

java:

/**
 * 评估逆波兰表达式。
 * 
 * @param tokens 代表逆波兰表达式的字符串数组,其中每个元素要么是整数,要么是四则运算符(+、-、*、/)
 * @return 逆波兰表达式的结果
 */
public int evalRPN(String[] tokens) {
    Stack<Integer> stack = new Stack<>();
    for(String s : tokens){
        // 遇到运算符时,执行相应的计算操作
        if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/"))
        {
            int b = stack.pop();
            int a = stack.pop();
            if(s.equals("+")) stack.push(a + b);
            else if (s.equals("-")) stack.push(a - b);
            else if (s.equals("*")) stack.push(a * b);
            else stack.push(a / b);
        }else{
            // 遇到数字时,将其转换为整数并入栈
            stack.push(Integer.valueOf(s));
        }
    }
    // 最后栈中剩余的数字即为表达式的计算结果
    return stack.pop();
}

  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值