算术表达式中最常见的表示法形式有 中缀、前缀和 后缀表示法。中缀表示法是书写表达式的常见方式,而前缀和后缀表示法主要用于计算机科学领域。
中缀表示法
中缀表示法是算术表达式的常规表示法。称它为 中缀表示法是因为每个操作符都位于其操作数的中间,这种表示法只适用于操作符恰好对应两个操作数的时候(在操作符是二元操作符如加、减、乘、除以及取模的情况下)。对以中缀表示法书写的表达式进行语法分析时,需要用括号和优先规则排除多义性。
Syntax: operand1 operator operand2 Example: (A+B)*C-D/(E+F) |
前缀表示法
前缀表示法中,操作符写在操作数的前面。这种表示法经常用于计算机科学,特别是编译器设计方面。为纪念其发明家 ― Jan Lukasiewicz(请参阅 参考资料),这种表示法也称 波兰表示法。
Syntax : operator operand1 operand2 Example : -*+ABC/D+EF |
后缀表示法
在后缀表示法中,操作符位于操作数后面。后缀表示法也称 逆波兰表示法(reverse Polish notation,RPN),因其使表达式求值变得轻松,所以被普遍使用。
Syntax : operand1 operand2 operator Example : AB+C*DEF+/- |
前缀和后缀表示法有三项公共特征:
- 操作数的顺序与等价的中缀表达式中操作数的顺序一致
- 不需要括号
- 操作符的优先级不相关
要把表达式从中缀表达式的形式转换成用后缀表示法表示的等价表达式,必须了解操作符的优先级和结合性。 优先级或者说操作符的强度决定求值顺序;优先级高的操作符比优先级低的操作符先求值。 如果所有操作符优先级一样,那么求值顺序就取决于它们的 结合性。操作符的结合性定义了相同优先级操作符组合的顺序(从右至左或从左至右)。
Left associativity : A+B+C = (A+B)+C Right associativity : A^B^C = A^(B^C) |
转换过程包括用下面的算法读入中缀表达式的操作数、操作符和括号:
- 初始化一个空堆栈,将结果字符串变量置空。
- 从左到右读入中缀表达式,每次一个字符。
- 如果字符是操作数,将它添加到结果字符串。
- 如果字符是个操作符,弹出(pop)操作符,直至遇见开括号(opening parenthesis)、优先级较低的操作符或者同一优先级的右结合符号。把这个操作符压入(push)堆栈。
- 如果字符是个开括号,把它压入堆栈。
- 如果字符是个闭括号(closing parenthesis),在遇见开括号前,弹出所有操作符,然后把它们添加到结果字符串。
- 如果到达输入字符串的末尾,弹出所有操作符并添加到结果字符串。
对后缀表达式求值比直接对中缀表达式求值简单。在后缀表达式中,不需要括号,而且操作符的优先级也不再起作用了。您可以用如下算法对后缀表达式求值:
- 初始化一个空堆栈
- 从左到右读入后缀表达式
- 如果字符是一个操作数,把它压入堆栈。
- 如果字符是个操作符,弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,后缀表达式的语法就不正确。
- 到后缀表达式末尾,从堆栈中弹出结果。若后缀表达式格式正确,那么堆栈应该为空。
实现代码:
2 /// <summary>
3 /// 中缀表达式转换为后缀表达式
4 /// </summary>
5 /// <param name="expression"></param>
6 /// <returns></returns>
7 public static string InfixToPostfix(string expression) {
8 Stack<char> operators = new Stack<char>();
9 StringBuilder result = new StringBuilder();
10 for (int i = 0; i < expression.Length; i++) {
11 char ch = expression[i];
12 if (char.IsWhiteSpace(ch)) continue;
13 switch (ch) {
14 case '+':
15 case '-':
16 while (operators.Count > 0) {
17 char c = operators.Pop(); //pop Operator
18 if (c == '(') {
19 operators.Push(c); //push Operator
20 break;
21 }
22 else {
23 result.Append(c);
24 }
25 }
26 operators.Push(ch);
27 result.Append(" ");
28 break;
29 case '*':
30 case '/':
31 while (operators.Count > 0) {
32 char c = operators.Pop();
33 if (c == '(') {
34 operators.Push(c);
35 break;
36 }
37 else {
38 if (c == '+' || c == '-') {
39 operators.Push(c);
40 break;
41 }
42 else {
43 result.Append(c);
44 }
45 }
46 }
47 operators.Push(ch);
48 result.Append(" ");
49 break;
50 case '(':
51 operators.Push(ch);
52 break;
53 case ')':
54 while (operators.Count > 0) {
55 char c = operators.Pop();
56 if (c == '(') {
57 break;
58 }
59 else {
60 result.Append(c);
61 }
62 }
63 break;
64 default:
65 result.Append(ch);
66 break;
67 }
68 }
69 while (operators.Count > 0){
70 result.Append(operators.Pop()); //pop All Operator
71 }
72 return result.ToString();
73 }
74 #endregion
/// <summary>
/// 求值的经典算法
/// </summary>
/// <param name="expression">字符串表达式</param>
/// <returns></returns>
public static double Parse(string expression)
{
string postfixExpression = InfixToPostfix(expression);
Stack<double> results = new Stack<double>();
double x, y;
for (int i = 0; i < postfixExpression.Length; i++)
{
char ch = postfixExpression[i];
if (char.IsWhiteSpace(ch)) continue;
switch (ch)
{
case '+':
y = results.Pop();
x = results.Pop();
results.Push(x + y);
break;
case '-':
y = results.Pop();
x = results.Pop();
results.Push(x - y);
break;
case '*':
y = results.Pop();
x = results.Pop();
results.Push(x * y);
break;
case '/':
y = results.Pop();
x = results.Pop();
results.Push(x / y);
break;
default:
int pos = i;
StringBuilder operand = new StringBuilder();
do
{
operand.Append(postfixExpression[pos]);
pos++;
} while (char.IsDigit(postfixExpression[pos]) || postfixExpression[pos] == '.');
i = --pos;
results.Push(double.Parse(operand.ToString()));
break;
}
}
return results.Peek();
}
#endregion