Leetcode小白试炼(20201210 基本计算器II)

一、题目

1. 题目描述

实现一个基本的计算器来计算一个简单的字符串表达式的值。

字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。

2. 示例

示例1:
输入: “3+2*2”
输出: 7
示例2:
输入: " 3/2 "
输出: 1
示例3:
输入: " 3+5 / 2 "
输出: 5
示例4:
输入:“1-1+1”
输出:1

3. 题目解析

见到计算器相关题目就想到了栈,但是处理这种包含运算符号优先级的题目,要小心。

二、个人解法

1. 解法分析

使用两个栈,一个存放数字,一个存放符号
处理方式:

  • 如果遇到的是数字,遍历到不是数字为止,将其拼接存到数字栈。
  • 如果遇到的是符号,判断符号栈的栈头是否为*或者/,如果是,则先将这个操作符给处理了(数字栈出俩数,配合这个操作符计算一个结果,并将结果入栈),再将遍历到的符号入符号栈;如果不是,则这个符号直接入栈。
  • 入栈结束后,遍历符号栈,数字栈配合其进行出栈运算,数字栈的最后一个元素就是我们要的结果。

2. 代码

    public int calculate(String s) {
        Stack<Integer> st1 = new Stack<>();
        Stack<Character> st2 = new Stack<>();
        int i = 0;
        int temp = 0;
        int result = 0;
        while (i < s.length()) {
//            去空格
            while (i < s.length() && s.charAt(i) == ' ') {
                i++;
            }
//            将操作符之前的数字拼接成一个整数
            while (i < s.length() && s.charAt(i) != '+' && s.charAt(i) != '-' && s.charAt(i) != '*' && s.charAt(i) != '/' && s.charAt(i) != ' ') {
                temp = 10 * temp + (s.charAt(i) - '0');
                i++;
            }
            st1.push(temp);
            while (i < s.length() && s.charAt(i) == ' ') {
                i++;
            }
//            遇到操作符
            if (i < s.length() && (s.charAt(i) == '+' || s.charAt(i) == '-' || s.charAt(i) == '*' || s.charAt(i) == '/')) {
//                如果栈顶元素是*
                if (!st2.empty() && st2.peek() == '*') {
                    st2.pop();
                    int temp1 = st1.pop() * st1.pop();
                    st1.push(temp1);
//                    st1.push(temp);
                    temp = 0;
                    st2.push(s.charAt(i));
//                    如果栈顶元素是/
                } else if (!st2.empty() && st2.peek() == '/') {
                    st2.pop();
                    int temp1 = st1.pop();
                    int temp2 = st1.pop();
                    st1.push(temp2 / temp1);
//                    st1.push(temp);
                    temp = 0;
                    st2.push(s.charAt(i));
                }
//                其他情况
                else {
//                    st1.push(temp);
                    temp = 0;
                    st2.push(s.charAt(i));
                }

            }

            i++;
        }
//        遍历操作符栈
        while (!st2.empty()) {
            char fu = st2.pop();
//            遇到不同的符号进行不同的操作
            if (fu == '+') {
                int temp1 = st1.pop();
                int temp2 = st1.pop();
//                result += temp1 + temp2;
                st1.push(temp2 + temp1);
            } else if (fu == '-') {
                int temp1 = st1.pop();
                int temp2 = st1.pop();
//                result += temp2 - temp1;
                st1.push(temp2 - temp1);
            } else if (fu == '*') {
                int temp1 = st1.pop();
                int temp2 = st1.pop();
//                result += temp2 * temp1;
                st1.push(temp2 * temp1);
            } else if (fu == '/') {
                int temp1 = st1.pop();
                int temp2 = st1.pop();
//                result += temp2 / temp1;
                st1.push(temp2 / temp1);
            }

        }
        return st1.pop();
    }

3. 失败反思

  • 严重超时,大概半小时才写出来,并且还是提交失败的错误代码。
  • 使用栈来对表达式进行从后往前计算,只考虑了运算符的优先级,未考虑到“1-1-1”、“1-1+1”这样的减号后面是加号或者减号这种情况。在这种情况中,如果从后往前运算,那应该是“1-(1+1)”和“1-(1-1)”即在代码中应该增加这种特殊情况的判断。

4. 改进

改进点

在最后遍历符号栈的时候,对出栈符号是+或者-,增加了判断条件,如果此时栈顶元素是-,那么这个操作符应该取反。

代码

存入栈的代码未改动,只写出有改动的代码:遍历操作符栈部分。

//        遍历操作符栈
        while (!st2.empty()) {
            char fu = st2.pop();
//            遇到不同的符号进行不同的操作
            if (fu == '+') {
                int temp1 = st1.pop();
                int temp2 = st1.pop();
//                如果此时栈顶元素是-,此符号取反
                if(!st2.empty()&&st2.peek()=='-'){
                    st1.push(temp2 - temp1);
                }
                else {
                    st1.push(temp2 + temp1);
                }
//                result += temp1 + temp2;

            } else if (fu == '-') {

                int temp1 = st1.pop();
                int temp2 = st1.pop();
//                如果此时栈顶元素是-,此符号取反
                if(!st2.empty()&&st2.peek()=='-'){
                    st1.push(temp2 + temp1);
                }
                else {
                    st1.push(temp2 - temp1);
                }
//                result += temp2 - temp1;
//                st1.push(temp2 - temp1);
            } else if (fu == '*') {
                int temp1 = st1.pop();
                int temp2 = st1.pop();
//                result += temp2 * temp1;
                st1.push(temp2 * temp1);
            } else if (fu == '/') {
                int temp1 = st1.pop();
                int temp2 = st1.pop();
//                result += temp2 / temp1;
                st1.push(temp2 / temp1);
            }

        }

5. 算法分析

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

6. 提交截图

(1)第一次失败截图

未考虑-+这样的符号排列
在这里插入图片描述

(2)第二次失败截图

修改完毕后,未考虑到--这样的符号排列
在这里插入图片描述

(3)第三次成功截图

在这里插入图片描述

三、官网高星解法

1. 单栈顺序处理法

(1)解法分析

  • 给第一个数补+号,把每个数和前面的操作符一起存到栈里(假如第一个数是负数,那存栈的第一个数是+0,第二个数是真正的第一个数)
  • 如果遇到*或/,出栈一个元素,两个元素做完操作后把结果再入栈
  • 如果遇到+或-,把符号和数拼接后入栈
    举例1:
    在这里插入图片描述
    该例中,最终栈内存放 +1、-12、+3
    举例2:
    在这里插入图片描述
    该例中,最终栈内存放 +2、-12(-3*4)、+5

(2)代码

   public int calculate(String s) {
        int result = 0;
        Stack<Integer> st1 = new Stack<>();
//        初始化符号变量为+,即假设第一个数不是负数的话给它一个+号
        char fu = '+';
        int i = 0;
        int temp = 0;
        while (i < s.length()) {
//            去除前空格
            while (i < s.length() && s.charAt(i) == ' ') {
                i++;
            }
//            连续的数字位拼接,得到待入栈整数
            while (i < s.length() && Character.isDigit(s.charAt(i))) {
                temp = temp * 10 + (s.charAt(i) - '0');
                i++;
            }
//            去除空格
            while (i < s.length() && s.charAt(i) == ' ') {
                i++;
            }
//            根据符号,来分支处理考虑这个待入栈整数如何入栈
            switch (fu) {
                case '+':
                    st1.push(temp);
                    break;
                case '-':
                    st1.push(-temp);
                    break;
                case '*':
                    int temp1 = st1.pop();
                    st1.push(temp1 * temp);
                    break;
                case '/':
                    int temp2 = st1.pop();
                    st1.push(temp2 / temp);
                    break;
            }
//            入栈结束后再处理空格
            while (i < s.length() && s.charAt(i) == ' ') {
                i++;
            }

//            获取下一个数的符号
            if (i < s.length()) {
                fu = s.charAt(i);
                temp = 0;
                i++;
            }

        }
//        栈内所有元素相加即为最终结果
        while (!st1.empty())
            result += st1.pop();
        return result;
    }

(3)算法分析

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

(4)精简代码

使用while循环是为了方便构思遍历到的每位字符。尝试改用for循环可以省去多次去空格。

 public int calculate(String s) {
        int result=0;
        Stack<Integer> st=new Stack<>();
        int temp=0;
        char fu='+';
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)==' '){
                continue;
            }
            else if(Character.isDigit(s.charAt(i))){
                temp=temp*10+(s.charAt(i)-'0');
            }
            else {
                switch (fu){
                    case '+':
                        st.push(temp);
                        break;
                    case '-':
                        st.push(-temp);
                        break;
                    case '*':
                        st.push(st.pop()*temp);
                        break;
                    case '/':
                        st.push(st.pop()/temp);
                        break;
                }
                fu=s.charAt(i);
                temp=0;
            }
        }
        //        栈内所有元素相加即为最终结果
        while (!st.empty())
            result += st.pop();
        return result;
    }

(5)提交截图

这是未精简版代码
在这里插入图片描述
这是精简版代码
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值