什么是前缀表达式,中缀表达式,后缀表达式?
以 3+4-5为例:
- 前缀表达式:操作符在操作数的前面,即 -+345
- 中缀表达式:操作符在操作数的中间,即 3+4-5
- 后缀表达式:操作符在操作数的后面,即 34+5-
为什么计算机对前缀 / 后缀表达式比较方便?
前缀 / 后缀表达式中不存在括号,通过前缀 / 后缀表达式可以很容易构造一颗运算树,通过该运算树,可以使用递归的方式很方便计算表达式。
将中缀表达式转为前/后缀表达式的简单转换方法:
设中缀表达式为 a + b * c - ( d + e )
step1:【加括号】为每一步运算加括号,即为 ( ( a + ( b * c ) ) - ( d + e ) )
step2:【移符号】将每一步括号内的运算符移动到该括号外
前缀表达式:- ( + ( a * ( b c ) ) + ( d e ) )
后缀表达式:( ( a ( b c ) * ) + ( d e ) + ) -
step3:【去括号】将表达式内所有括号移除
前缀表达式:- + a * b c + d e
后缀表达式:a b c * + d e + -
中缀表达式转后缀表达式
- 初始化两个栈,一个是操作符栈s1,一个是操作数栈s2;
- 从左至右遍历中缀表达式;
- 当为操作数时,直接压入s2;
- 当为( 时,直接压入s1;
- 当为 )时,将s1栈顶弹出并压入s2,直至遇到(,并舍弃该对括号;
- 当为操作符时,如果s1为空 或 s1栈顶为( 或 当前操作符优先级大于栈顶,直接压入s1;
- 当为操作符时,如果s1不为空且s1栈顶不为( 且当前操作符优先级不大于栈顶,将s1栈顶弹出并压入s2,直至满足6;
- 重复2-7,直至遍历完中缀表达式;
- 将s1内元素弹出压入s2;
- 弹出s2,逆序即为后缀表达式。
package yrwan05;
public class InfixToSuffix {
/**
* 将中缀表达式转为后缀表达式
*
* @param in 传入中缀表达式
* @return 后缀表达式
*/
public static CharStack change(String in) {
CharStack s1 = new CharStack(in.length());// 操作符栈
CharStack s2 = new CharStack(in.length());// 操作数栈
// 开始判断
for (int i = 0; i < in.length(); i++) {
System.out.print("s1栈元素为(栈底--->栈顶):");
s1.display();
System.out.print("s2栈元素为(栈底--->栈顶):");
s2.display();
char ch = in.charAt(i);
System.out.println("当前待判断的字符:" + ch);
if ((ch <= '9' && ch >= '0') || (ch <= 'z' && ch >= 'a')) {// 遇到数压入s2
s2.push(ch);
} else if (ch == '(') {// 遇到左括号,直接压入s1
s1.push(ch);
} else if (ch == ')') {// 遇到右括号,将s1弹出并压入s2,直至s1遇到左括号,并舍弃这对括号
char temp;
while ((temp = s1.pop()) != '(') {
s2.push(temp);
}
} else {// 遇到运算符
// 若s1非空 并且 s1的栈顶不是"(" 并且 ch优先级不大于s1的栈顶
while (!s1.isEmpty() && s1.peek() != '(' && !compare(ch, s1.peek())) {
s2.push(s1.pop());// 将s1弹到s2,直至满足下方条件
}
// 若s1为空 或 s1的栈顶是"(" 或 ch优先级大于s1的栈顶
s1.push(ch);
}
}
// 中缀表达式遍历完成,将s1的元素弹到s2
while (!s1.isEmpty()) {
s2.push(s1.pop());
}
return s2;
}
/**
* 比较两运算符优先级
*
* @param a 传入待比较
* @param b 传入栈顶
* @return true是a大,false是a小或相等
*/
public static boolean compare(char a, char b) {
if ((a == '*' || a == '/') && (b != '*' || b != '/')) {
return true;
} else {
return false;
}
}
}
中缀表达式转前缀表达式
- 初始化两个栈,一个是操作符栈s1,一个是操作数栈s2;
- 从右至左遍历中缀表达式;
- 当为操作数时,直接压入s2;
- 当为 )时,直接压入s1;
- 当为( 时,将s1栈顶弹出并压入s2,直至遇到 ),并舍弃该对括号;
- 当为操作符时,如果s1为空 或 s1栈顶为)或 当前操作符优先级大于等于栈顶,直接压入s1;
- 当为操作符时,如果s1不为空且s1栈顶不为) 且当前操作符优先级小于栈顶,将s1栈顶弹出并压入s2,直至满足6;
- 重复2-7,直至遍历完中缀表达式;
- 将s1内元素弹出压入s2;
- 弹出s2,即为前缀表达式。
package yrwan05;
public class InfixToPrefix {
/**
* 将中缀表达式转为前缀表达式
*
* @param in 传入中缀表达式
* @return 前缀表达式
*/
public static CharStack change(String in) {
CharStack s1 = new CharStack(in.length());// 操作符栈
CharStack s2 = new CharStack(in.length());// 操作数栈
// 开始判断
for (int i = in.length() - 1; i >= 0; i--) {
System.out.print("s1栈元素为(栈底--->栈顶):");
s1.display();
System.out.print("s2栈元素为(栈底--->栈顶):");
s2.display();
char ch = in.charAt(i);
System.out.println("当前待判断的字符:" + ch);
if ((ch <= '9' && ch >= '0') || (ch <= 'z' && ch >= 'a')) {// 遇到数压入s2
s2.push(ch);
} else if (ch == ')') {// 遇到右括号,直接压入s1
s1.push(ch);
} else if (ch == '(') {// 遇到左括号,将s1弹出并压入s2,直至s1遇到右括号,并舍弃这对括号
char temp;
while ((temp = s1.pop()) != ')') {
s2.push(temp);
}
} else {// 遇到运算符
// 若s1非空 并且 s1的栈顶不是")" 并且 ch优先级小于s1的栈顶
while (!s1.isEmpty() && s1.peek() != ')' && compare(s1.peek(), ch)) {
s2.push(s1.pop());// 将s1弹到s2,直至满足下方条件
}
// 若s1为空 或 s1的栈顶是")" 或 ch优先级大于等于s1的栈顶
s1.push(ch);
}
}
// 中缀表达式遍历完成,将s1剩余的元素弹到s2
while (!s1.isEmpty()) {
s2.push(s1.pop());
}
while (!s2.isEmpty()) {
s1.push(s2.pop());
}
return s1;
}
/**
* 比较两运算符优先级
*
* @param a 传入待比较
* @param b 传入栈顶
* @return true是a大,false是a小或相等
*/
public static boolean compare(char a, char b) {
if ((a == '*' || a == '/') && (b != '*' || b != '/')) {
return true;
} else {
return false;
}
}
}
CharStack类为:
package yrwan05;
/**
* 用于存放运算符与运算数的栈
* @author Wyran
*
*/
public class CharStack {
private char[] arr;
private int top;
public CharStack(int size) {
arr = new char[size];
top = -1;
}
public void push(char c) {
if (!isFull()) {
arr[++top] = c;
}
}
public char pop() {
return arr[top--];
}
public char peek() {
return arr[top];
}
public boolean isEmpty() {
return top == -1;
}
public boolean isFull() {
return top == arr.length - 1;
}
// 为了便于后面分解展示栈中的内容,增加了一个遍历栈的方法
public void display() {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
测试类为:
package yrwan05;
import java.util.Scanner;
public class ITStest {
public static void main(String[] args) {
CharStack result;
System.out.print("输入中缀表达式:");
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
result =InfixToSuffix.change(input);// 转为后缀表达式
System.out.print("\n转换后的后缀表达式为:");
result.display();
System.out.println("-----------------------------------");
result = InfixToPrefix.change(input);// 转为前缀表达式
System.out.print("\n转换后的前缀表达式为:");
result.display();
sc.close();
}
}