如何优雅的写出字符串计算器(java 递归篇)(代码可直接使用)

如上,在上一篇中,我们使用中缀转后缀的形式完成字符串计算器的功能,那么有的同学说,我不懂中缀后缀啊,我就想直接算,行不行啊? 行,请看下文。

这次我们使用的思路是递归,也是我在面试过程中最先想到的,但是面试的时候比较慌,20分钟没有理清思路。

这次我们先讲整体思路,
第一步:将整个字符串划分为数字和符号,保存到一个list中,
第二步:设计一个返回值为INT类型的函数,在函数内部实现两个功能,去括号和计算加减乘除,这样我们的函数进行递归,即可进行多括号的加减乘除运算。

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

 public static boolean isNum(char c){
    
    
        return c>=48 && c<=57;
    }

其次,我们将每个数字和运算符都转化为一段String类型的对象存储到List中,

public static List<String> inorder (String str) {
    
    
        ArrayList<String> list = new ArrayList<>();
        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||(i<x.length-1&&!isNum(x[i+1]))){
    
    
                    list.add(stringBuilder.toString());
                    stringBuilder.delete(0,stringBuilder.length());
                }
            }else {
    
    
                list.add(String.valueOf(x[i]));
            }
        }
        return list;
    }

对于每个数字,我们判断后方是否是符号,或者是最后一位,如果是这两种情况,就将字符串存入list,并且清空StringBuilder。
如此 对于例子 12+2*(4-3) 我们得到的list应该是这样
[12, +, 2, *, (, 4, -, 3, )]

以上,我们完成了将一整个字符串拆分为数字和符号的对象集合,第二步,我们进行计算。(这里采用递归思想)

Stack<String> stack = new Stack<>();
        int len = list.size();
        for (int i=0;i<len;i++){
    
    
            String x = list.get(i);
            if (x.equals(")"))
            {
    
    
                List<String> lis = new ArrayList<>();
                while (!stack.isEmpty()&&!stack.peek().equals("(")){
    
    
                    lis.add(0,stack.pop());
                }
                stack.pop();
                String m = String.valueOf(help(lis));
                stack.push(m);
                continue;
            }
            stack.push(list.get(i));
        }

这是我们计算函数的第一部分,用来去除括号,具体思想如下: 从遍历list,如果当前的对象是一个“)”,那么我们需要进行匹配,新建立一个list,从辅助栈中出栈,直到遇到一个(,那么我们完成了一次括号匹配,lis中存入的应该是括号内所有的内容。
递归- 当前函数应该返回一个计算结果,且传入值为一个list类型,所以我们将lis,也就是括号内的一个序列,调用自身,那么我们应该得到了括号内的计算结果,将其加入辅助栈中。如此就完成了去括号。
第二 -如果当前对象不是),那么我们将其入栈。

接下来是计算部分

 Stack<String> res = new Stack<>();
        while (!stack.isEmpty()){
    
    
            String x = stack.pop();
            if (x.equals("*")){
    
    
                Integer num1 = Integer.valueOf(stack.pop());
                Integer num2 = Integer.valueOf(res.pop());
                res.push(String.valueOf(num1*num2));
                continue;
            }else if (x.equals("/")){
    
    
                Integer num1 = Integer.valueOf(stack.pop());
                Integer num2 = Integer.valueOf(res.pop());
                res.push(String.valueOf(num1/num2));
                continue;
            }
            res.push(x);
        }

我们新建一个辅助栈 res,对乘除进行匹配,遇到乘除符号的时候,我们应该从stack和res中分别poll出一个对象,然后使用/*计算结果,再加入到res中。
如果当前对象不是乘除符号,我们就直接把它加入到res中。

最后一步,计算加减

 while (!res.isEmpty()){
    
    
            String x = res.pop();
            if (x.equals("+")){
    
    
                Integer num1 = Integer.valueOf(res.pop());
                Integer num2 = Integer.valueOf(stack.pop());
                stack.push(String.valueOf(num1+num2));
                continue;
            }else if (x.equals("-")){
    
    
                Integer num1 = Integer.valueOf(res.pop());
                Integer num2 = Integer.valueOf(stack.pop());
                stack.push(String.valueOf(num2-num1));
                continue;
            }
            stack.push(x);
        }

思想与上一步计算乘除相同,需要注意的是,在计算除法的时候,我们从stack中取出的对象是被除数,而结果全部转移到res中时,我们的序列被反转,那么计算减法的时候,应该使用stack中的对象去减res中的对象。

完整代码

import java.util.*;
public class Main {
    
    
    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        String str= input.next();
        List<String> list = inorder(str);
        System.out.println(help(list));


    }
    public static Integer help(List<String> list){
    
    
        Stack<String> stack = new Stack<>();
        int len = list.size();
        for (int i=0;i<len;i++){
    
    
            String x = list.get(i);
            if (x.equals(")"))
            {
    
    
                List<String> lis = new ArrayList<>();
                while (!stack.isEmpty()&&!stack.peek().equals("(")){
    
    
                    lis.add(0,stack.pop());
                }
                stack.pop();
                String m = String.valueOf(help(lis));
                stack.push(m);
                continue;
            }
            stack.push(list.get(i));
        }

        Stack<String> res = new Stack<>();
        while (!stack.isEmpty()){
    
    
            String x = stack.pop();
            if (x.equals("*")){
    
    
                Integer num1 = Integer.valueOf(stack.pop());
                Integer num2 = Integer.valueOf(res.pop());
                res.push(String.valueOf(num1*num2));
                continue;
            }else if (x.equals("/")){
    
    
                Integer num1 = Integer.valueOf(stack.pop());
                Integer num2 = Integer.valueOf(res.pop());
                res.push(String.valueOf(num1/num2));
                continue;
            }
            res.push(x);
        }
        while (!res.isEmpty()){
    
    
            String x = res.pop();
            if (x.equals("+")){
    
    
                Integer num1 = Integer.valueOf(res.pop());
                Integer num2 = Integer.valueOf(stack.pop());
                stack.push(String.valueOf(num1+num2));
                continue;
            }else if (x.equals("-")){
    
    
                Integer num1 = Integer.valueOf(res.pop());
                Integer num2 = Integer.valueOf(stack.pop());
                stack.push(String.valueOf(num2-num1));
                continue;
            }
            stack.push(x);
        }
        return Integer.valueOf(stack.pop());

    }

    public static boolean isNum(char c){
    
    
        return c>=48 && c<=57;
    }
    public static List<String> inorder (String str) {
    
    
        ArrayList<String> list = new ArrayList<>();
        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||(i<x.length-1&&!isNum(x[i+1]))){
    
    
                    list.add(stringBuilder.toString());
                    stringBuilder.delete(0,stringBuilder.length());
                }
            }else {
    
    
                list.add(String.valueOf(x[i]));
            }
        }
        return list;
    }
}

如上,我们就完成了用递归思想编写的java计算器,希望同学们能够学到一些东西吧。 PS:同样的代码在leetcode上居然跑不通,在3/2的用例上报错,但是我在本地跑又是对的。奇怪
PS2:原来leetcode上那道计算器是不带括号的,(lll¬ω¬)
PS3:突然想起,大三时候JAVA课的编程作业就是写一个计算器,但是当时是从网上找的代码,后悔呀。
PS4:又发现一点小问题,留给同学们自己修改, 在第一次匹配乘除的时候,应该按顺序计算,我的程序是倒着计算的,导致连序出现乘除的时候,结果会出现错误。

猜你喜欢

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