如何优雅的写出一个字符串计算器(JAVA)

字符串计算器

坐标2020-4-24
经过-华为二面
面试官给了一张动态图,问我:二十分钟能写出一个简单的字符串计算器吗?
略微思考:应该可以吧
接着在混乱的思路中,想用递归,又想用栈,最后写出一个无数空指针异常的计算器,结束后,痛定思痛,今天就教大家写一个字符串计算器。

首先,我们一定需要一个函数来判断字符是否是数字,

 public static boolean isNum(char x){
    
    
        return x>=48 && x<=57;
    }//利用ascall码判断是否为数字

有了它,我们进行第二步,设计算法思路。
字符串计算器的解题思路呢,我在网上只看见一种,就是用中缀波兰表达式转化为后缀,然后逐个计算。而在我面试的过程中,我的第一反应是使用递归,将所有括号缩减,然后按先乘除,后加减的顺序计算,今天我们将两个思路都进行完善。

思路1:波兰表达式
首先我们看如下表达式 2+3*(6-2) 这个式子,也就是我们输入的式子,就是中缀波兰表达式,我们将它转化为后缀表达式,得到的应该是 2 3 6 2 - * +
大家发现什么规律了呢? 也就是将运算符按运算顺序放到数字的后面,我们计算的时候只需要用一个栈来辅助,就可以按顺序计算出来结果了。
有的小伙伴说,思路我懂,怎么写呢?且听我慢慢道来。

第一步,我们需要一些辅助工具,
1.字符串

StringBuilder stringBuilder = new StringBuilder();

使用StringBuilder来存储转化完成的后缀表达式。

2.栈

Stack<Character> stack = new Stack<>();

我们用栈来辅助存储。

3.原表达式的字符形式

char x[] = str.toCharArray();

我们把传入函数的字符串转化为字符型数组,这样方便使用isNum函数判断

那么现在函数就是这个样子

扫描二维码关注公众号,回复: 12176290 查看本文章
 public static String inorder (String str){
    
    
        Stack<Character> stack = new Stack<>();
        char x[] = str.toCharArray();
        StringBuilder stringBuilder = new StringBuilder();
        retrun null;
        }

下一步,我们遍历整个字符型数组

 public static String inorder (String str){
    
    
        Stack<Character> stack = new Stack<>();
        char x[] = str.toCharArray();
        StringBuilder stringBuilder = new StringBuilder();
        for(int i=0;i<x.length;i++){
    
    

        }
        retrun null;
        }

现在想想,我们遍历数组的过程中应该完成什么操作呢?
有以下几种情况
1.当前字符为数字
2.当前字符为运算符
3.当前字符为括号

在情况1中,我们只需要将其加入结果,因为我们最后的后缀表达式中,数字是按照原有顺序排列的。

 public static String inorder (String str){
    
    
        Stack<Character> stack = new Stack<>();
        char x[] = str.toCharArray();
        StringBuilder stringBuilder = new StringBuilder();
        for(int i=0;i<x.length;i++){
    
    
            if (isNum(x[i])) {
    
     //该字符为数字,直接加入结果
                stringBuilder.append(x[i]);
                continue;
            }
        }
        retrun null;
        }

有的同学说:那要是多位数怎么办呢?我们进行一些改进

 public static String inorder (String str){
    
    
        Stack<Character> stack = new Stack<>();
        char x[] = str.toCharArray();
        StringBuilder stringBuilder = new StringBuilder();
        for(int i=0;i<x.length;i++){
    
    
            if (isNum(x[i])) {
    
     //该字符为数字,直接加入结果
                stringBuilder.append(x[i]);
                if ((i<x.length-1&&!isNum(x[i+1]))||i==x.length-1)){
    
    
                    stringBuilder.append(" ");
                }
                continue;
            }
        }
        retrun null;
        }

这一步,我们将数字加入结果后,进行一个判断,如果当前字符已经是最后一个字符,我们就给它后面加入一个空格,或者呢,当前字符的后一个字符不是数字了,那么我们也将它后面加一个空格。

情况2和3
在运算过程中,/符号的优先级是比±更高的,所以我们先处理/,当我们遇到*/,就将其放入辅助的栈内

 if (x[i]=='*'||x[i]=='/'){
    
    
                stack.push(x[i]);
                continue;
            }

括号的优先级比乘除更高,怎么办呢?
思考栈的特性,我们需要在遇到左括号的时候入栈,暂且认为它们和乘除是同级的 ,如下

 if (x[i]=='*'||x[i]=='/'||x[i]=='('){
    
    
                stack.push(x[i]);
                continue;
            }

在遇到左括号以后,我们仍然进行原有的逻辑操作,直到发现一个右括号,也就是括号匹配规则,
这时候,我们需要对栈进行操作,循环出栈,直到找到跟这个右括号对应的左括号,我们就完成了一个括号内的转化,

if (x[i]==')'){
    
    
                while (!stack.isEmpty()){
    
    
                    if (stack.peek()=='('){
    
    
                        stack.pop();
                        continue;
                    }
                    stringBuilder.append(stack.pop());
                }
                continue;
            }

有的同学可能觉得不太形象, 让我们模拟一下栈的内容,
(3+4)遇到右括号之前
栈的内容是 ( +
而字符串内容是 3 4
那么我们出栈操作后,字符串的内容变成了 3 4 +
而栈内不存在括号内的运算符了。
此时,我们完成了一个括号内的后缀转换。

这时候杠精(划掉)–同学们又说了,那加减呢?
当遇到加减的时候,我们要考虑几种情况
1.当前栈为空,直接将符号入栈
2.当前栈不为空,栈顶元素为(,说明±是括号内的运算符,我们也直接入栈,
3.当前栈不为空,栈顶元素不是(,说明±可能在括号内,但当前运算顺序的±应该是排在栈顶元素的后面,我们进行出栈后,再将±放入栈内

if (x[i]=='+'||x[i]=='-'){
    
    
               while (!stack.isEmpty()&&stack.peek()!='('){
    
    
                   stringBuilder.append(stack.pop());
               }
               stack.push(x[i]);
               continue;
            }

最后,我们加上返回语句,就得到了后缀表达式

return stringBuilder.toString();

完整函数如下

public static String inorder (String str){
    
    
        Stack<Character> stack = new Stack<>();
        char x[] = str.toCharArray();
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0;i<x.length;i++){
    
    
            if (isNum(x[i])) {
    
    
                stringBuilder.append(x[i]);
                if (((i<x.length-1&&!isNum(x[i+1]))||i==x.length-1)){
    
    
                    stringBuilder.append(" ");
                }
                continue;
            }
            if (x[i]=='*'||x[i]=='('||x[i]=='/'){
    
    
                stack.push(x[i]);
                continue;
            }
            if (x[i]==')'){
    
    
                while (!stack.isEmpty()){
    
    
                    if (stack.peek()=='('){
    
    
                        stack.pop();
                        continue;
                    }
                    stringBuilder.append(stack.pop());
                }
                continue;
            }
            if (x[i]=='+'||x[i]=='-'){
    
    
               while (!stack.isEmpty()&&stack.peek()!='('){
    
    
                   stringBuilder.append(stack.pop());
               }
               stack.push(x[i]);
               continue;
            }

        }
        return stringBuilder.toString();
    }

下一步,将后缀表达式计算出结果,这一步很简单,我们不进行讲解,只需要将数字入栈,遇到运算符进行出栈,获得计算结果后再次入栈。
注意 /和- 需要将出栈的两个数字调换顺序,才能计算出正确结果

public static Integer help(String str){
    
    
        Stack<Integer> num = new Stack<>();
        StringBuilder stringBuilder = new StringBuilder();
        char x[] = str.toCharArray();
        for (int i =0;i<x.length;i++){
    
    
            if (isNum(x[i])){
    
    
                stringBuilder.append(x[i]);
                if (i+1==x.length){
    
    
                    num.push(Integer.parseInt(stringBuilder.toString()));
                }
                if (x[i+1]==' '){
    
    
                    num.push(Integer.parseInt(stringBuilder.toString()));
                    stringBuilder.delete(0,stringBuilder.length());
                    continue;
                }
            }
            if (x[i]=='+'){
    
    
                num.push(num.pop()+num.pop());
            }
            if (x[i]=='-'){
    
    
                int a = num.pop();
                int b = num.pop();
                num.push(b-a);
            }
            if (x[i]=='*'){
    
    
                num.push(num.pop()*num.pop());
            }
            if (x[i]=='/'){
    
    
                int a = num.pop();
                int b = num.pop();
                num.push(b/a);
            }
        }
        return num.pop();
    }

如此,我们在主函数中调用两个函数,即可得到最后的计算结果。

递归请看第二篇文章。

猜你喜欢

转载自blog.csdn.net/qq_45789385/article/details/105733553