基于中缀表达式和后缀表达式的计算器
中缀表达式计算器
使用栈完成表达式的计算思路:
-
设置两个空栈:
- 数栈 n u m S t a c k numStack numStack:用于存放数
- 运算符栈 o p e r a t o r S t a c k operatorStack operatorStack:用于存放运算符
-
通过一个 i n d e x index index值遍历表达式
-
若为数,则直接入数栈
-
若为运算符,分以下情况:
- 如果当前符号栈为空,则直接入符号栈
- 如果符号栈有操作符,就进行比较
- 当前操作符的优先级小于等于栈中操作符,从数栈中pop出两个数,在运算符栈中pop出一个符号进行运算,将运算结果压入数栈,然后将当前运算符压入运算符栈.
- 当前运算符的优先级大于栈中的运算符,直接压入运算符栈
-
当表达式扫描完毕,就顺序从数栈和符号栈中pop出响应的数和符号,并运行
-
最后数栈中只剩下一个数字,就是表达式的结果
缺点:不方便识别括号
前缀、中缀、后缀表达式
前缀表达式
例如: ( 3 + 4 ) × 5 − 6 (3+4)\times5-6 (3+4)×5−6 对应的前缀表达式为: − × + 3 4 5 6 -\,\times\,+\,3\,4\,5\,6 −×+3456
计算机求值:
-
设置一个数栈
-
从右到左扫描表达式,遇到数字的时候,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用当前运算符对它们做相应的计算(栈顶元素和次顶元素,注意顺序:栈顶元素为 n u m 1 num1 num1,次顶元素为 n u m 2 num2 num2),并将结果压入栈;
-
重复上述过程,最后栈中只剩下一个元素,将其pop出来作为运算结果
中缀表达式
中缀表达式就是我们平时所见到的表达式,如: ( 3 + 4 ) × 5 − 6 (3+4)\times5-6 (3+4)×5−6
中缀表达式的求值是我们人最熟悉的,但是对于计算机来讲并不好操作.因此在计算结果时,往往会将中缀表达式转化为其他表达式来操作(一般是转化成后缀表达式)
后缀表达式(逆波兰表达式)
后缀表达式又称为逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后(最符合计算机的思考方式)
举例说明:
( 3 + 4 ) × 5 − 6 (3+4)\times5-6 (3+4)×5−6对应的后缀表达式为 3 4 + 5 × 6 − 3\,4\,+\,5\,\times\,6\,- 34+5×6−
计算过程:
与前缀相似,只不过是从前往后扫描
中缀表达式 ->
后缀表达式
思路步骤分析:
- 初始化两个栈,运算符栈 s 1 s_1 s1和存储中间结果的栈 s 2 s_2 s2
- 从左到右扫描中缀表达式
- 遇到操作数直接压入 s 2 s_2 s2
- 遇到运算符,比较其与
s
1
s_1
s1栈顶运算符的优先级(小括号不纳入运算符)
- 如果 s 1 s_1 s1为空,或者栈顶元素为左括号"(",则直接将当前运算符压入 s 1 s_1 s1
- 若优先级高于 s 1 s_1 s1栈顶运算符,也将当前运算符压入 s 1 s_1 s1
- 若不满足以上两个条件,将 s 1 s_1 s1栈顶元素弹出并压入 s 2 s_2 s2中,再次转至4.1,当前运算符与新的栈顶运算符相比较
- 遇到括号:
- 如果是左括号"(",则直接压入 s 1 s_1 s1
- 如果是右括号")“,则依次弹出 s 1 s_1 s1栈顶的运算符,并压入 s 2 s_2 s2,直到遇到左括号”(“,此时将这一对括号丢弃(这样的操作决定了 s 1 s_1 s1中不可能有右括号”)")
- 重复步骤2~5,直至表达式的最右边
- 将 s 1 s_1 s1中剩余的运算符依次弹出并压入 s 2 s_2 s2
- 依次弹出 s 2 s_2 s2的元素并输出,结果的逆序即为对应的后缀表达式
简化:注意 s 2 s_2 s2在整个操作期间没有pop操作,只有压栈操作,故没必要再设定一个 s 2 s_2 s2栈(结束后需要全部弹出并且逆序).故 s 2 s_2 s2可以直接设置成 A r r a y L i s t ArrayList ArrayList以省去弹出并逆序的操作.
中缀表达式计算器
完整代码:
import java.util.ArrayList;
import java.util.Stack;
public class Calculator
{
public static void main(String[] args)
{
Calculator calculator = new Calculator("1+2*7.5-3/2");
System.out.println(calculator.getResult());
}
//算式字符串
String formula;
//构造方法
public Calculator(String formula)
{
this.formula = formula;
}
public double getResult()
{
//数栈
Stack<Double> numStack = new Stack<>();
//运算符栈
Stack<String> operStack = new Stack<>();
//所有元素放再elements中
ArrayList<String> elements = divideElement();
int cur = 0;
while(cur < divideElement().size())
{
String s = elements.get(cur);
//是数字直接压入数字栈
if(!isOperator(s))
{
numStack.push(Double.parseDouble(s));
}
else
{
//运算符栈为空,则直接压入运算符栈
if(operStack.isEmpty())
{
operStack.push(s);
cur++;
continue;
}
String oper1 = s; //oper1为当前运算符
String oper2 = operStack.peek(); //oper2为运算符栈顶运算符
if(compareOper(oper1,oper2) == -1) //oper1优先级小于等于oper2
{
//先从数栈中弹出两个数,进行计算后,压入数栈
oper2 = operStack.pop();
double num2 = numStack.pop();
double num1 = numStack.pop();
numStack.push(calculate(num1,oper2,num2));
//再将当前运算符压入运算符栈
operStack.push(oper1);
}
else //oper1优先级大于oper2
{
operStack.push(oper1);
}
}
cur++;
}
//将数栈和运算符栈中所有的元素取出,一一计算
while(!operStack.isEmpty())
{
double num2 = numStack.pop();
double num1 = numStack.pop();
String oper = operStack.pop();
numStack.push(calculate(num1,oper,num2));
}
//最后数栈中剩下的元素即为结果
return numStack.pop();
}
//将所有元素(数,运算符)拆分至一个ArrayList中
public ArrayList<String> divideElement()
{
int cur = 0;
ArrayList<String> elements = new ArrayList<>();
while(cur < formula.length())
{
Character c = formula.charAt(cur);
//运算符只有一个字符,直接压入
if(isOperator(c))
{
elements.add(c.toString());
cur++;
}
//默认除运算符以外的所有元素都为数字
else
{
//捕获的所有数字符号拼接到一个StringBuilder中
StringBuilder sb = new StringBuilder();
while(cur < formula.length() && !isOperator(formula.charAt(cur)) )
{
sb.append(formula.charAt(cur));
cur++;
}
//将StringBuilder转化为字符串添加至ArrayList中
elements.add(sb.toString());
}
}
return elements;
}
//判断是否为运算符
//字符
public boolean isOperator(Character c) { return c == '+' || c == '-' || c == '*' || c == '/'; }
//字符串(重写)
public boolean isOperator(String s) { return s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/"); }
//比较运算符优先级(oper1 - oper2),oper1大于oper2则返回1,否则返回-1
public static int compareOper(String oper1,String oper2)
{
//如果运算符1为"+"或"-",则无论oper2是什么,oper1的优先级都不会比oper2大,返回-1
if(oper1.equals("+") || oper1.equals("-"))
{
return -1;
}
else
{
if(oper2.equals("+") || oper2.equals("-"))
{
return 1;
}
else
{
return -1;
}
}
}
//计算两数
public static double calculate(double num1,String oper,double num2)
{
switch (oper)
{
case "+": { return num1 + num2; }
case "-": { return num1 - num2; }
case "*": { return num1 * num2; }
case "/": { return num1 / num2; }
//只能识别加减乘除,其他的预算符则抛出异常
default: { throw new RuntimeException("运算符无效!"); }
}
}
}
后缀表达式计算器
完整代码:
import java.util.ArrayList;
import java.util.Stack;
public class PolandCalculator
{
public static void main(String[] args)
{
PolandCalculator calculator = new PolandCalculator("(1+2.5)*3-4*((5-1*2)-2)");
System.out.println(calculator.getResult());
}
//算式字符串
String formula;
//构造方法
public PolandCalculator(String formula)
{
this.formula = formula;
}
//将所有元素(数,运算符)拆分至一个ArrayList中
public ArrayList<String> divideElement()
{
int cur = 0;
ArrayList<String> elements = new ArrayList<>();
while(cur < formula.length())
{
Character c = formula.charAt(cur);
if(isOperator(c) || c == '(' || c == ')')
{
elements.add(c.toString());
cur++;
}
else
{
StringBuilder sb = new StringBuilder();
while(cur < formula.length() && !(isOperator(formula.charAt(cur)) || formula.charAt(cur)=='(' || formula.charAt(cur)==')') )
{
sb.append(formula.charAt(cur));
cur++;
}
elements.add(sb.toString());
}
}
return elements;
}
//中缀表达式转后缀表达式
public ArrayList<String> infixToSuffix(ArrayList<String> infix)
{
Stack<String> s1 = new Stack<>();
//s2没有弹出操作,因此可以直接定义成ArrayList(因为如果使用栈的话,全部弹出后还需要反转)
ArrayList<String> s2 = new ArrayList<>();
int cur = 0;
while(cur < infix.size())
{
String s = infix.get(cur);
//如果是操作数,直接压入s2
if(!(isOperator(s) || s.equals("(") || s.equals(")")))
{
s2.add(s);
}
else
{
//是运算符
if(isOperator(s))
{
while(true)
{
//s1为空,或者当前运算符优先级大于s1栈顶运算符
if (s1.isEmpty() || s1.peek().equals("(") || compareOper(s, s1.peek()) == 1)
{
s1.push(s);
break;
}
//运算符优先级小于等于s1栈顶运算符,将s1栈顶弹出并交付至s2中,继续循环
else
{
s2.add(s1.pop());
}
}
}
//是括号
else
{
//左括号直接压入
if(s.equals("("))
{
s1.push(s);
}
//右括号进行以下操作
else
{
while(!s1.peek().equals("("))
{
s2.add(s1.pop());
}
s1.pop();
}
}
}
cur++;
}
while(!s1.isEmpty())
{
s2.add(s1.pop());
}
//s2即为最终的后缀表达式,返回s2
return s2;
}
//计算结果
public double getResult()
{
ArrayList<String> infix = divideElement();
ArrayList<String> suffix = infixToSuffix(infix);
//用一个栈存储中间结果
Stack<Double> numStack = new Stack<>();
for (String s:suffix)
{
//是运算符则取出两数进行计算
if(isOperator(s))
{
double num2 = numStack.pop();
double num1 = numStack.pop();
numStack.push(calculate(num1,s,num2));
}
//是数则直接转化成double压入栈
else
{
numStack.push(Double.parseDouble(s));
}
}
return numStack.pop();
}
//判断是否为运算符
//字符
public boolean isOperator(Character c)
{
return c == '+' || c == '-' || c == '*' || c == '/';
}
//字符串(重写)
public boolean isOperator(String s) { return s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/"); }
//比较运算符优先级(oper1 - oper2),oper1大于oper2则返回1,否则返回-1
public static int compareOper(String oper1,String oper2)
{
//如果运算符1为"+"或"-",则无论oper2是什么,oper1的优先级都不会比oper2大,返回-1
if(oper1.equals("+") || oper1.equals("-"))
{
return -1;
}
else
{
if(oper2.equals("+") || oper2.equals("-"))
{
return 1;
}
else
{
return -1;
}
}
}
//计算两数
public static double calculate(double num1,String oper,double num2)
{
switch (oper)
{
case "+": { return num1 + num2; }
case "-": { return num1 - num2; }
case "*": { return num1 * num2; }
case "/": { return num1 / num2; }
//只能识别加减乘除,其他的预算符则抛出异常
default: { throw new RuntimeException("运算符无效!"); }
}
}
}