中序表达式:
中序表达式就是我们日常使用的表达式,由左往右阅读,结构清晰,但需要括号改变优先级,对计算机不友好
eg: (1+4)*3+10/5
前序表达式(波兰表示法Polish notation,或波兰记法):
一种逻辑、算术和代数表示方法,其特点是操作符置于操作数的前面,如果操作符的元数(arity)是固定的,则语法上不需要括号仍然能被无歧义地解析,不需要括号来改变优先级,未推广使用。
eg:
中序表达式:(1 + 4) * 3 + 10 / 5
前序表达式: + * + 1 4 3 / 10 5
前序表达式的运算:
eg:
+ * + 1 4 3 / 6 5
运算: + 1 4 => 1 + 4 = 5
* 5 3 => 5 * 3 = 15
新前序: + 15 / 10 5
/ 10 5 => 10 / 5 =2
新前序: + 15 2
=> 15 + 2 = 17
后序表达式(逆波兰表示法(Reverse Polish notation,RPN,或逆波兰记法):
所有操作符置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级,使用广泛。艾兹格·迪科斯彻引入了调度场算法,用于将中缀表达式转换为后缀形式。因其操作类似于火车编组场而得名。 大多数操作符优先级解析器(解析器用简单的查表操作即可实现,优先级表由开发者自己定制,在不同的应用场景中,开发者可自由改变操作符的优先级)能转换为处理后缀表达式,实际中,一般构造抽象语法树,树的后序遍历即为逆波兰记法。
eg:
中序表达式:(1 + 4) * 3 + 10 / 5
后序表达式: 1 4 + 3 * 10 5 / +
后序表达式的运算:
eg:
1 4 + 3 * 10 5 / +
运算: 1 4 + => 1 + 4 = 5
5 3 * => 5 * 3 = 15
新后序: 15 / 10 5 +
10 5 / => 10 / 5 =2
新后序: 15 2 +
=> 15 + 2 = 17
中序转后序(调度场算法)
- 创建一个队列和一个操作数栈
- 遇到操作数则送入队列
- 遇到操作符则送入栈
- 当碰到与栈顶同优先级的操作符,则将栈顶的操作符取出,送入队列,并将新操作符压入栈中
- 遇到” )“括号则将栈内从”( “到” )“的所有操作符全部取出送入队列
- 表达式分类完成后,先输出队列内容,再输出栈内容,前者+后者便是后序表达式
实现代码(Java版):
public static String InfixToPostfix(String s) {
Stack<Character> stack = new Stack<Character>();
Queue<Character> queue = new Queue<Character>();
Character s1 = ' ';
char peeks = ' '; // 栈顶元素
for (int i = 0; i < s.length(); i++) {
// 如果是操作数,则送入操作数队列
if (s.charAt(i) >= 48 && s.charAt(i) <= 57) {
queue.enqueue(s.charAt(i) );
continue;
}
// 如果碰到右括号则遍历栈,并送入队列,直到碰到"("
if (s.charAt(i) == ')') {
s1 = stack.pop();
while (s1 != '(' ) {
queue.enqueue(s1);
s1 = stack.pop();
}
continue;
}
// 如果出现同优先级的运算符, 则将栈顶元素送入队列中 同时新运算符送入栈内
if ( (s.charAt(i) == '/' || s.charAt(i) == '*') &&
(peeks == '*' || peeks == '/') ) {
queue.enqueue(stack.pop());
stack.push(s.charAt(i));
peeks=s.charAt(i);
}
// 剩下的普通情况下的操作符,送入操作符栈
{
stack.push(s.charAt(i) );
peeks = s.charAt(i);
}
}
s = " ";// 初始化s来接收表达式
//接收队列的值
do {
s += queue.dequeue() +" ";
} while (!queue.isEmpty());
//接受栈的值
do {
s += stack.pop() + " ";
} while (!stack.isEmpty());
return s;
}