前缀表达式(Polish expression, 波兰表达式)
前缀表达式
前缀表达式的运算符位于操作数之前。 (3+4)×5-6 对应的前缀表达式就是 - × + 3 4 5 6。
运算过程
例如:(3+4)×5-6 对应的前缀表达式就是 - × + 3 4 5 6 , 针对前缀表达式求值步骤如下:
- 从右至左扫描,将6、5、4、3压入堆栈
- 遇到+运算符,因此弹出3和4(3为栈顶元素,4为次顶元素),计算出3+4的值,得7,再将7入栈
- 接下来是×运算符,因此弹出7和5,计算出7×5=35,将35入栈
- 最后是-运算符,计算出35-6的值,即29,由此得出最终结果
中缀表达式
中缀表达式就是常见的运算表达式,如(3+4)×5-6。此表达式需判断运算符的优先级。中缀表达式的求值是我们人最熟悉的,但是对计算机来说却不好操作(前面我们讲的案例就能看的这个问题,因为中缀表达式存在运算符优先级的问题),因此,在计算结果时,往往会将中缀表达式转成其它表达式来操作(一般转成后缀表达式)。
中缀表达式(Infix expression): 实现简单计算器
思路分析
- 定义两个栈: 一个是数值栈, 另一个运算符栈
- 定义两个方法: 判断运算符优先级的方法和计算已入栈数值的方法
- 逐个循环扫描输入的中缀表达式, 如果是数字就入数值栈, 如果是运算符, 则需要与运算符栈的栈顶运算符比较优先级. 如果优先级高于栈顶的运算符, 则直接入栈(运算符栈), 否则, 从数值栈取2元素(数值), 在从运算符栈取栈顶元素做运算, 将运算结果再存入数值栈, 之后将当前运算符入栈(运算符栈)
/** 定义数组栈*/
class ArrayStack {
/** 栈大小*/
private int maxSize;
/** 通过该数组存放数据, 模拟栈数据结构*/
private int[] stack;
/** 栈顶的 index, 初始值为-1*/
private int top = -1;
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack = new int[maxSize];
}
/** 栈满*/
public boolean isFull() {
return top == maxSize - 1;
}
/** 栈空*/
public boolean isEmpty() {
return top == -1;
}
/** 入/压栈*/
public void push(int value) {
if (isFull()) {
System.out.println("入栈失败, 栈已满!");
return;
}
top++;
stack[top] = value;
}
/** 出/弹栈*/
public int pop() {
if (isEmpty()) {
throw new RuntimeException("出栈失败, 没有数据!");
}
int value = stack[top];
top--;
return value;
}
/** 从栈顶开始打印所有内容*/
public void list() {
if (isEmpty()) {
System.out.println("打印失败, 没有数据!");
return;
}
for (int i = top; i >= 0; i--) {
System.out.printf("stack[%d]=%d\n", i, stack[i]);
}
}
/** 查看栈顶元素内容*/
public int peek() {
return stack[top];
}
/** 运算符的优先级*/
public int operPriority(int oper) {
if (oper == '*'|| oper == '/') {
return 1;
} else if(oper == '+'|| oper == '-') {
return 0;
} else {
/** 无效的表达式*/
return -1;
}
}
/** 判断是不是一个运算符*/
public boolean isOper(char val) {
return val == '+' || val=='-' || val=='*' || val=='/';
}
/** 计算方法*/
public int cal(int num1, int num2, int oper) {
int res = 0;
switch (oper) {
case '+':
res = num1 + num2;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
}
public class CalculatorApp {
public static void main(String[] args) {
System.out.println("请输入要计算的中缀表达式: ");
Scanner in = new Scanner(System.in);
String expression = in.nextLine();
in.close();
/** 定义数值栈*/
ArrayStack numStack = new ArrayStack(10);
/** 定义运算符栈*/
ArrayStack operStack = new ArrayStack(10);
/** 表达式每个字符位索引*/
int index = 0;
/** 每次循环获取到的字符*/
char ch;
/** 计算多位数时, 用于拼接的字符串变量*/
String keepnum = "";
/** 当计算时, 从数值栈出栈的第一个数值*/
int num1 = 0;
/** 当计算时, 从数值栈出栈的第而二个数值*/
int num2 = 0;
/** 运算符 char <-> int*/
int oper = 0;
/** 计算结果*/
int res = 0;
while (true) {
/** 每次循环获取单个字符*/
ch = expression.substring(index, index + 1).charAt(0);
/** 判断是否为运算符*/
if (operStack.isOper(ch)) {
if (operStack.isEmpty()) {
/** 如果定义运算符栈为空, 直接入栈*/
operStack.push(ch);
} else {
/** 判断当前运算符(如 +)的优先级是否低于栈顶运算符(如 x), 如果低或相等通过*/
if (operStack.operPriority(ch) <= operStack.operPriority(operStack.peek())) {
/** 之前已入栈的数值与运算符取出, 开始计算*/
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = operStack.cal(num1, num2, oper);
/** 数值栈的累计值计算后, 将值重新入栈*/
numStack.push(res);
/** 当前运算符(如 +), 入栈*/
operStack