Java实现四则运算带括号和负数

题目

请实现如下接口

    /* 功能:四则运算

     * 输入:strExpression:字符串格式的算术表达式,如: “3+2*{1+2*[-4/(8-6)+7]}”

         * 返回:算术表达式的计算结果

     */

    public static int calculate(String strExpression)

    {

        /* 请实现*/

        return 0;

    } 

约束:

  1. pucExpression字符串中的有效字符包括[‘0’-‘9’],‘+’,‘-’, ‘*’,‘/’ ,‘(’, ‘)’,‘[’, ‘]’,‘{’ ,‘}’。

  2. pucExpression算术表达式的有效性由调用者保证; 

 

输入描述:

输入一个算术表达式

输出描述:

得到计算结果

示例1

输入

3+2*{1+2*[-4/(8-6)+7]}

输出

25

有两种常见的实现方式,这里为了简洁没有对错误输入进行判断.

  1. 先将中缀表达式转化为后缀表达式(逆波兰式),然后再计算后缀表达式两个栈直接计算
  2. 两栈直接计算

注意事项

  • 这里一定注意负数的处理,因为这一点困惑了很久
  • 在输入的表达式两端中加入 () 可以帮助 判断是否表达式结束 eg:1+1 => (1+1) 这样在遍历时可以通过括号的匹配,注意括号必须成对出现
  • 先将表达式中的[] {} 替换为 () 方便计算 
package com.example;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		String s = sc.next();	
		//System.out.println(s.substring(1));
		calculate(s);
	}
	//负数的问题
	public static int calculate(String strExpression)
    {
		String s = simplify(strExpression);
		System.out.println("s : "+s);
		String numStr = "";//记录数字
		Stack<Character> opeStack = new Stack<>();//符号站		
		int l = s.length();//字符串长度 l
		List<String> list = new ArrayList<>();
		
		for(int i=0;i<l;i++)
		{
			char ch = s.charAt(i);
			
			if(isAllOpe(ch))				
			{	
				if(numStr!="")
				{
					list.add(numStr);
					numStr="";
				}
				
				
				if(ch=='(')
				{
					opeStack.push(ch);					
				}
				else if(isOpe(ch))
				{
					char top = opeStack.peek();
					if(isGreater(ch, top))						
					// ch优先级大于top 压栈
					{
						opeStack.push(ch);
					}
					else
					//否则,将栈内元素出栈,直到遇见 '(' 然后将ch压栈
					{						
						while(true)	
						//必须先判断一下 后出栈 否则会有空栈异常
						{	
							char t=opeStack.peek();
							if(t=='(')
								break;							
							if(isGreater(ch, t))
								break;
							
							list.add(Character.toString(t));
							t=opeStack.pop();
						}
						opeStack.push(ch);
						
					}
	
				}
				else if(ch==')')
				{
					char t = opeStack.pop();				
					while(t!='('&&!opeStack.isEmpty())
					{
						list.add(Character.toString(t));
						t = opeStack.pop();
					}	
				}

			}
			else//处理数字
			{
				numStr+=ch;
			}
		}
	
		//计算后缀表达式	
		System.out.println(list.toString());
		Stack<Integer> num = new Stack<>();
		int size = list.size();
		for(int i=0;i<size;i++)
		{
			String t =list.get(i);
			if(isNumeric(t))
			{//将t转换成int 方便计算
				num.push(Integer.parseInt(t));
			}
			else
			{
				//如果t为运算符则 只有一位
				char c = t.charAt(0);
				int b = num.pop();
				//如果有 算式是类似于 -8-8 这样的需要判断一下栈是否为空
					int a = num.pop();					
					switch(c)
					{
						case '+':
							num.push(a+b);
							break;
						case '-':
							num.push(a-b);
							break;
						case '*':
							num.push(a*b);
							break;
						case '/':
							num.push(a/b);
							break;
						default:
							break;		
					}
		}
		}
		System.out.println(num.pop());
		return 0;
    }
	
	
	/**化简表达式
	 * 将表达式中的 {}[]替换为()
	 * 负数的处理
	 * 为了方便将中缀转换为后缀在字符串前后分别加上(,) eg:"1+1" 变为"(1+1)"
	 * @param str 输入的字符串
	 * @return s 返回简化完的表达式
	 */
	public static String simplify(String str)
	{
		//负数的处理
		// 处理负数,这里在-前面的位置加入一个0,如-4变为0-4,
		// 细节:注意-开头的地方前面一定不能是数字或者反括号,如9-0,(3-4)-5,这里地方是不能加0的
		// 它的后面可以是数字或者正括号,如-9=>0-9, -(3*3)=>0-(3*3)
		String s = str.replaceAll("(?<![0-9)}\\]])(?=-[0-9({\\[])", "0");	
		//将表达式中的 {}[]替换为()
		s = s.replace('[', '(');
		s = s.replace('{', '(');
		s = s.replace(']', ')');
		s = s.replace(']', ')');
		//为了方便将中缀转换为后缀在字符串前后分别加上(,)
		s="("+s+")";
		
		return s ;
	}
	
	/**判断字符c是否为合理的运算符
	 * 
	 * @param c
	 * @return
	 */
	public static boolean isOpe(char c)
	{
		if(c=='+'||c=='-'||c=='*'||c=='/')
			return true;
		else		
			return false;
	}
	
	public static boolean isAllOpe(char c)
	{
		if(c=='+'||c=='-'||c=='*'||c=='/')
			return true;
		
		else if(c=='('||c==')')
			return true;
		else		
			return false;
	}
	
	/**
	 * 比较字符等级a是否大于b
	 * @param a
	 * @param b
	 * @return 大于返回true 小于等于返回false
	 */
	public static boolean isGreater(char a,char b)
	{	
			int a1 = getLevel(a);
			int b1 = getLevel(b);
			if(a1>b1)
				return true;
			else
				return false;
	}
	
	/**
	 * 得到一个字符的优先级
	 * @param a
	 * @return
	 */
	public static int getLevel(char a)
	{

		if(a=='+')
			return 0;
		else if(a=='-')
			return 1;
		else if(a=='*')			
			return 3;
		else if(a=='/')
			return 4;
		else 
			return -1;

	}

	//判断是不是数字
	 public static boolean isNumeric(String str){
         Pattern pattern = Pattern.compile("[0-9]*");
         Matcher isNum = pattern.matcher(str);
         if( !isNum.matches() ){
             return false;
         }
         return true;
  }

}

方法2 两个栈直接计算

链接:https://www.nowcoder.com/questionTerminal/9999764a61484d819056f807d2a91f1e
来源:牛客网
//转自牛客网 这里我也加了一些自己的分析
public class Main{
    // 用于存放一个正括号的集合, 用于简化代码
    static Set<Character> brace = new HashSet<>();
    public static void main(String ... args){
        Scanner sc = new Scanner(System.in);
        // 初始化正括号集合 他这里处理括号的方式有些不懂,我是直接替换的    
   brace.add('{');
        brace.add('(');
        brace.add('[');
        while(sc.hasNextLine()){
            // 对字符串做初始化处理,原则有二:
            // 1、处理负数,这里在-前面的位置加入一个0,如-4变为0-4,
            // 细节:注意-开头的地方前面一定不能是数字或者反括号,如9-0,(3-4)-5,这里地方是不能加0的
            // 它的后面可以是数字或者正括号,如-9=>0-9, -(3*3)=>0-(3*3)
            // 2、处理字符串,在最后的位置加#, 主要是为了防止最后一个整数无法处理的问题
            String exp = sc.nextLine().replaceAll("(?<![0-9)}\\]])(?=-[0-9({\\[])", "0") + "#";
            System.out.println(calculate(exp));
        }
    }
    private static int calculate(String exp){
        // 初始化栈
        Stack<Integer> opStack = new Stack<>();
        Stack<Character> otStack = new Stack<>();
         
        // 整数记录器  当num是多位时将 num += c 知道遇见运算符
        String num = "";
        for(int i = 0; i < exp.length(); i++){
            // 抽取字符
            char c = exp.charAt(i);
            // 如果字符是数字,则加这个数字累加到num后面
            if(Character.isDigit(c)){
                num += c;
            }
            // 如果不是数字
            else{
                // 如果有字符串被记录,则操作数入栈,并清空
                if(!num.isEmpty()){
                    int n = Integer.parseInt(num);
                    num = "";
                    opStack.push(n);
                }
                // 如果遇上了终结符则退出
                if(c == '#')
                    break;
                // 如果遇上了+-
                else if(c == '+' || c == '-'){
                    // 空栈或者操作符栈顶遇到正括号,则入栈
                    if(otStack.isEmpty() || brace.contains(otStack.peek())){
                        otStack.push(c);
                    } else {
                        // 否则一直做弹栈计算,直到空或者遇到正括号为止,最后入栈
                        while(!otStack.isEmpty() && !brace.contains(otStack.peek()))
                            popAndCal(opStack, otStack);
                        otStack.push(c);
                    }
                }
                // 如果遇上*/
                else if(c == '*' || c == '/'){
                    // 空栈或者遇到操作符栈顶是括号,或者遇到优先级低的运算符,则入栈
                    if(otStack.isEmpty()
                            || brace.contains(otStack.peek())
                            || otStack.peek() == '+' || otStack.peek() == '-'){
                        otStack.push(c);
                    }else{
                        // 否则遇到*或/则一直做弹栈计算,直到栈顶是优先级比自己低的符号,最后入栈
                        while(!otStack.isEmpty()
                                && otStack.peek() != '+' && otStack.peek() != '-'
                                && !brace.contains(otStack.peek()))
                            popAndCal(opStack, otStack);
                        otStack.push(c);
                    }
                } else {
                    // 如果是正括号就压栈
                    if(brace.contains(c))
                        otStack.push(c);
                    else{
                        // 反括号就一直做弹栈计算,直到遇到正括号为止
                        char r = getBrace(c);
                        while(otStack.peek() != r){
                            popAndCal(opStack, otStack);
                        }
                        // 最后弹出正括号
                        otStack.pop();
                    }
                }
            }
        }
        // 将剩下的计算完,直到运算符栈为空
        while(!otStack.isEmpty())
            popAndCal(opStack, otStack);
        // 返回结果
        return opStack.pop();
    }
    private static void popAndCal(Stack<Integer> opStack, Stack<Character> otStack){
        int op2 = opStack.pop();
        int op1 = opStack.pop();
        char ot = otStack.pop();
        int res = 0;
        switch(ot){
            case '+':
                res = op1 + op2;
                break;
            case '-':
                res = op1 - op2;
                break;
            case '*':
                res = op1 * op2;
                break;
            case '/':
                res = op1 / op2;
                break;
        }
        opStack.push(res);
    }
    private static char getBrace(char brace){
        switch(brace){
            case ')':
                return '(';
            case ']':
                return '[';
            case '}':
                return '{';
        }
        return '#';
    }
}

点赞