题目
请实现如下接口
/* 功能:四则运算
* 输入:strExpression:字符串格式的算术表达式,如: "3+2*{1+2*[-4/(8-6)+7]}"
* 返回:算术表达式的计算结果
*/
public static int calculate(String strExpression)
{
/* 请实现*/
return 0;
}
约束:
pucExpression字符串中的有效字符包括[‘0’-‘9’],‘+’,‘-’, ‘*’,‘/’ ,‘(’, ‘)’,‘[’, ‘]’,‘{’ ,‘}’。
pucExpression算术表达式的有效性由调用者保证;
输入描述:
输入一个算术表达式
输出描述:
得到计算结果
示例1
输入
3+2*{1+2*[-4/(8-6)+7]}
输出
25
有两种常见的实现方式,这里为了简洁没有对错误输入进行判断.
- 先将中缀表达式转化为后缀表达式(逆波兰式),然后再计算后缀表达式两个栈直接计算
- 两栈直接计算
注意事项
- 这里一定注意负数的处理,因为这一点困惑了很久
- 在输入的表达式两端中加入 () 可以帮助 判断是否表达式结束 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 '#';
}
}