C++算法题 - 栈

20. 有效的括号

LeetCode_link


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

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1
输入:s = “()”
输出:true

示例 2
输入:s = “()[]{}”
输出:true

示例 3
输入:s = “(]”
输出:false

提示
1 <= s.length <= 10^4
s 仅由括号 '()[]{}' 组成


思路

class Solution {
private:
    unordered_map<char, char> pair = {
        {'}', '{'},
        {')', '('},
        {']', '['}
    };
public:
    bool isValid(string s) {
        stack<char> st;
        int n = s.size();
        for(char & c : s){
            if(pair.find(c) != pair.end()){
                //是右括号
                if(st.empty() || st.top() != pair[c])   return false;
                st.pop();
            }else if(c == '{' || c == '(' || c == '['){
                st.push(c);
            }
        }
        if(!st.empty()) return false;
        return true;

    }
};

71. 简化路径

LeetCode_link


给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/' 开头),请你将其转化为更加简洁的规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//')都被视为单个斜杠 '/' 。 对于此问题,任何其他格式的点(例如,'...')均被视为文件/目录名称。

请注意,返回的 规范路径 必须遵循下述格式:

  • 始终以斜杠 '/' 开头。
  • 两个目录名之间必须只有一个斜杠 '/'
  • 最后一个目录名(如果存在)不能 以 '/' 结尾。
  • 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.''..')。

返回简化后得到的 规范路径

示例 1
输入:path = “/home/”
输出:“/home”
解释:注意,最后一个目录名后面没有斜杠。

示例 2
输入:path = “/…/”
输出:“/”
解释:从根目录向上一级是不可行的,因为根目录是你可以到达的最高级。

示例 3
输入:path = “/home//foo/”
输出:“/home/foo”
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。

示例 4
输入:path = “/a/./b/…/…/c/”
输出:“/c”

提示
1 <= path.length <= 3000
path 由英文字母,数字,'.''/''_' 组成。
path 是一个有效的 Unix 风格绝对路径。


思路:分两步,放入栈中,出栈并组成路径结果

class Solution {
public:
    string simplifyPath(string path) {
        int n = path.size();
        int left = 0, right = 0;
        stack<string> s;
        //放入栈中
        while(right < n){
            while(right < n && path[right] != '/'){
                right ++;
            }
            if(left != right){
                string temp = path.substr(left, right - left);
                if(temp != ".") {
                    s.push(temp);
                }
            }
            right ++;
            left = right;
        }
        if(s.empty())   return "/";
        int count_return = 0;
        string rec = "";
        while(!s.empty()){
            if(s.top() == ".."){
                count_return ++;
                s.pop();
            }else if(count_return > 0){
                s.pop();
                count_return --;
            }else{
                rec = '/' + s.top() + rec;
                s.pop();
            }
        }
        if(rec == "")   return "/";
        return rec;

    }
};

155. 最小栈

LeetCode_link


设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

示例 1:
输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

提示
-2^31 <= val <= 2^31 - 1
poptopgetMin 操作总是在 非空栈 上调用
push, pop, top, and getMin最多被调用 3 * 10^4


思路:用辅助栈辅助存储最小信息
在这里插入图片描述

辅助栈存放从小到大的排序栈(耗时耗内存)

class MinStack {
private:
    stack<int> s;
    stack<int> sort_s;
public:
    MinStack() {

    }
    
    void push(int val) {
        s.push(val);
        stack<int> temp;
        while(!sort_s.empty() && sort_s.top() < val){
            temp.push(sort_s.top());
            sort_s.pop();
        }
        sort_s.push(val);
        while(!temp.empty()){
            sort_s.push(temp.top());
            temp.pop();
        }
    }
    
    void pop() {
        int top = s.top();
        s.pop();
        stack<int> temp;
        while(sort_s.top() != top){
            temp.push(sort_s.top());
            sort_s.pop();
        }
        sort_s.pop();
        while(!temp.empty()){
            sort_s.push(temp.top());
            temp.pop();
        }
    }
    
    int top() {
        return s.top();
    }
    
    int getMin() {
        return sort_s.top();
    }
};

辅助栈存放min(当前值,原全栈最小)

class MinStack {
private:
    stack<int> s;
    stack<int> min_s;
public:
    MinStack() {
        min_s.push(INT_MAX);
    }
    
    void push(int val) {
        s.push(val);
        min_s.push(min(val, min_s.top()));
    }
    
    void pop() {
        s.pop();
        min_s.pop();
    }
    
    int top() {
        return s.top();
    }
    
    int getMin() {
        return min_s.top();
    }
};

150. 逆波兰表达式求值

LeetCode_link


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

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

注意

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

示例 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

提示:
1 <= tokens.length <= 10^4
tokens[i] 是一个算符("+""-""*""/"),或是在范围 [-200, 200] 内的一个整数

逆波兰表达式
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。

  • 平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 )
  • 该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * )

逆波兰表达式主要有以下两个优点:

  • 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
  • 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

思路:栈用来存放数字,遇到运算符就跳出最顶上的两个进行运算。注意,除法,第一个弹出的是除数,第二个才是被除数。

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> s;
        for(string& a : tokens){
            if(a != "+" && a != "-" && a != "*" && a != "/"){
                s.push(stoi(a));
            }else{
                int x = s.top();
                s.pop();
                int y = s.top();
                s.pop();
                s.push(compute(y, x, a));
            }
        }
        return s.top();
    }
    int compute(int x, int y, string com){
        if(com == "+")  return x + y;
        else if(com == "-") return x - y;
        else if(com == "*") return x * y;
        else if(com == "/") return x / y;
        return 0;
    }
};

224. 基本计算器

LeetCode_link


给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval()

示例 1
输入:s = “1 + 1”
输出:2

示例 2
输入:s = " 2-1 + 2 "
输出:3

示例 3
输入:s = “(1+(4+5+2)-3)+(6+8)”
输出:23

提示
1 <= s.length <= 3 * 10^5
s 由数字、'+''-''('')'、和 ' ' 组成
s 表示一个有效的表达式
'+' 不能用作一元运算(例如, "+1""+(2 + 3)" 无效)
'-' 可以用作一元运算(即 "-1""-(2 + 3)" 是有效的)
输入中不存在两个连续的操作符
每个数字和运行的计算将适合于一个有符号的 32位 整数


思路:一开始是用递归写的,遇到左括号就进新递归,右括号返回上一个递归。

class Solution {
private:
    int i = 0;
public:
    int calculate(string s) {
        int n = s.size();
        int rec = 0;
        rec = compute_left(s);
        return rec;
    }
    int compute_left(string s){
        int sum = 0;
        int sign = 1;
        int n = s.size();
        for(; i < n; i++){
            if(s[i] >= '0' && s[i] <= '9'){
                //是数字
                int number = s[i] - '0';
                while(i+1 < n && s[i+1] >= '0' && s[i+1] <= '9'){
                    i++;
                    number = number * 10 + (s[i] - '0');
                }
                sum += sign * number;
            }else if(s[i] == '+'){
                sign = 1;
            }else if(s[i] == '-'){
                sign = -1;
            }else if(s[i] == '('){
                i++;
                sum += sign * compute_left(s);
            }else if(s[i] == ')'){
                return sum;
            }
        }
        return sum;
    }
};

思路:将公式展开进行计算,每次到一个符号的时候,如果栈的长度不是1,那么前面一定有括号,就存在括号内的符号可能会发生变化的情况。
在这里插入图片描述

class Solution {
public:
    int calculate(string s) {
        stack<int> sta;
        sta.push(1);
        int sum = 0;
        int one_num = 0;
        int op = 1;
        for(char& c : s){
            if(c >= '0' && c <= '9'){
                one_num = one_num * 10 + (c - '0');
                continue;
            }
            sum += op * one_num;
            one_num = 0;
            if(c == '+')    op = sta.top();
            else if(c == '-') op = -sta.top();
            else if(c == '(')  sta.push(op);
            else if(c == ')')   sta.pop();
        }
        sum += op * one_num;
        return sum;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

x_fengmo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值