中缀表达式转换为后缀表达式的算法示意图代码示例
文章目录
概念
中缀表达式
中缀表达式是数学表达式的一种常见写法,其中操作符位于操作数之间。与中缀表达式相对的是前缀表达式和后缀表达式(也称为逆波兰表达式)。
中缀表达式的概念和用途如下:
-
直观性:中缀表达式在书写和阅读上更直观和熟悉,因为我们通常习惯了使用中缀表示法来表示数学运算。例如,将"3 + 5"表示为中缀表达式比表示为前缀或后缀表达式更容易理解。
-
人类可读性:中缀表达式更接近自然语言的书写形式,使得人们更容易阅读和理解表达式。这在教学、文档编写和交流中特别有用。
-
运算符优先级:中缀表达式采用了常见的运算符优先级规则,例如乘除高于加减。这使得我们可以直接使用常见的数学规则来计算中缀表达式,无需额外的解析或转换步骤。
-
简化输入:对于简单的数学运算,中缀表达式可以提供一种简化输入的方式。例如,当我们需要计算简单的数学问题时,只需编写类似于"2 + 3 * 4"的中缀表达式即可,而无需显式地指定运算符优先级。
-
与计算机编程的关联:许多编程语言和计算机科学领域的工具都支持中缀表达式作为输入形式。通过将中缀表达式转换为前缀或后缀表达式,我们可以更方便地进行计算和处理。
需要注意的是,虽然中缀表达式在人类交流和理解方面有优势,但在计算机内部执行和计算时,前缀和后缀表达式更常见和有效。因此,在实际计算中,通常会将中缀表达式转换为前缀或后缀形式来进行处理和求值。
总之,中缀表达式是一种常见且直观的数学表达方式,适用于人类理解和书写数学运算。它在教学、文档编写和简化输入等方面非常有用。同时,通过将中缀表达式转换为前缀或后缀形式,我们可以更方便地进行计算和处理。
后缀表达式
逆波兰表达式(Reverse Polish Notation,RPN),也称为后缀表达式,是一种不使用括号来表示运算符优先级的数学表达式写法。在逆波兰表达式中,操作符位于操作数之后。
逆波兰表达式的特点和用途如下:
-
无需括号:逆波兰表达式不需要括号来明确运算符的优先级。通过将操作符放置在操作数之后,可以避免歧义和括号解析问题。
-
简化计算:逆波兰表达式可以直接进行计算,无需考虑运算符优先级。我们可以按照从左到右的顺序依次处理每个操作符,并使用栈数据结构来保存操作数和计算结果。
-
易于转换:逆波兰表达式可以相对容易地从中缀表达式或前缀表达式进行转换。通过转换为逆波兰表达式,我们可以更方便地进行计算和处理复杂的数学表达式。
-
计算器实现:逆波兰表达式常用于计算器的设计和实现。通过将用户输入的中缀表达式转换为逆波兰表达式,计算器可以简化计算过程并减少运算符优先级的困扰。
-
栈的应用:逆波兰表达式的求值可以使用栈来实现。通过将操作数入栈,遇到操作符时从栈中弹出相应数量的操作数进行计算,并将结果重新入栈。这种栈的应用使得逆波兰表达式的计算更加高效和简单。
逆波兰表达式的一个示例是:“3 4 + 2 *”
在这个表达式中,先计算3和4的和(得到7),然后将结果7和2相乘(得到14)。注意,这个表达式无需括号来明确运算顺序。
逆波兰表达式在编程语言、计算器设计和计算机科学领域有广泛的应用。它提供了一种简洁和直观的方式来表示和计算数学表达式,并且避免了括号解析和运算符优先级的问题。
前缀表达式
前缀表达式,也称为波兰表达式,是一种数学表达式的写法,其中操作符位于操作数之前。与前缀表达式相对的是中缀表达式和后缀表达式。
前缀表达式的概念和用途如下:
-
简化计算:前缀表达式可以直接进行计算,无需进行括号解析或考虑运算符优先级。这使得计算机在执行表达式时更加高效和简单。
-
方便转换:前缀表达式相对容易从中缀表达式或后缀表达式进行转换。通过转换为前缀表达式,我们可以更方便地进行计算和处理复杂的数学表达式。
-
函数式编程:前缀表达式与函数式编程范式密切相关。在一些函数式编程语言中,函数调用和操作符都采用前缀形式,例如Lisp和Scheme。前缀表达式适应了这种函数式编程的特点,并提供了简洁和一致的语法。
-
编译器设计和解释器实现:前缀表达式在编译器设计和解释器实现中有广泛应用。通过使用前缀表达式,编译器可以更容易地生成中间代码或进行语法分析。解释器可以直接按照前缀表达式的顺序执行操作符和操作数,无需解析括号或考虑优先级。
下面是一个使用前缀表达式进行简单算术运算的示例:
原始中缀表达式 | 对应前缀表达式 |
---|---|
2 + 3 | + 2 3 |
(4 * 5) - 6 | - * 4 5 6 |
7 / (8 + 9) | / 7 + 8 9 |
通过将中缀表达式转换为前缀表达式,我们可以直接按照操作符和操作数的顺序进行计算,无需担心运算符优先级或括号的影响。这种方式更加直观和简单,并且适用于编程语言、编译器设计和解释器实现等领域。
总之,前缀表达式是一种简化计算、方便转换和与函数式编程相关的数学表达方式。它在编程语言、编译器设计和解释器实现中有广泛的应用。通过使用前缀表达式,我们可以更高效地进行数学运算和处理复杂的表达式。
中转后流程图:
开始
├─ 1初始化两个栈: 运算符栈S1和储存中间结果的栈S2
├─ 2从左至右扫描中缀表达式
│ ├─ 3遇到操作数时,将其压入S2
│ └─ 4遇到运算符时,比较其与S1栈顶运算符的优先级
│ ├─ 4.1如果S1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈
│ ├─ 4.2否则,若优先级比栈顶运算符的高,也将运算符压入S1
│ └─ 4.3否则,将S1栈顶的运算符弹出并压入到S2中,再次比较4.1与S1中新的栈顶运算符相比较
├─ 5遇到括号时
│ ├─ 5.1如果是左括号“(”则直接压入S1
│ └─ 5.2如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃
├─ 6重复步骤(2)至(5),直到表达式的最右边
├─ 7将S1中剩余的运算符依次弹出并压入S2
└─ 8依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
结束
这个流程图展示了将中缀表达式转换为后缀表达式的过程。从开始到结束,按照流程图的步骤进行操作,可以将给定的中缀表达式转换为后缀表达式。
中转后示例示意图
输入:中缀表达式 (3+4)*5
初始化:
运算符栈 S1: 空
储存中间结果的栈 S2: 空
从左至右扫描中缀表达式:
-------------------------------------------------------
| 字符 | S1 | S2 | 操作 |
-------------------------------------------------------
| ( | ( | | 将 ( 入栈 |
-------------------------------------------------------
| 3 | ( | 3 | 将 3 入栈 |
-------------------------------------------------------
| + | (+ | 3 | 将 + 入栈 |
-------------------------------------------------------
| 4 | (+ | 34 | 将 4 入栈 |
-------------------------------------------------------
| ) | | 34+ | 弹出 S1 中的 ( |
-------------------------------------------------------
| * | * | 34+ | 将 * 入栈 |
-------------------------------------------------------
| 5 | * | 34+5 | 将 5 入栈 |
-------------------------------------------------------
遍历完中缀表达式,将 S1 中剩余的运算符依次弹出并压入 S2:
-------------------------------------------------------
| 字符 | S1 | S2 | 操作 |
-------------------------------------------------------
| * | | 34+5* | 弹出 *,压入 S2 |
-------------------------------------------------------
输出 S2 中的元素,结果的逆序即为后缀表达式:
34+5*
因此,中缀表达式 (3+4)*5 转换为后缀表达式 34+5*
通过示意图,你可以更直观地看到每一步的操作和栈的变化。这有助于理解将中缀表达式转换为后缀表达式的算法过程。
逐步分析
步骤1:初始化两个栈:运算符栈S1和储存中间结果的栈S2;
**原因:**我们使用两个栈来处理中缀表达式转换为后缀表达式的过程。S1用于存储运算符,而S2用于存储中间结果。
步骤2:从左至右扫描中缀表达式;
**原因:**我们按照从左到右的顺序扫描中缀表达式,以便逐个处理其中的每个字符。
步骤3:遇到操作数时,将其压入S2;
原因:当我们遇到一个操作数(数字)时,我们将其直接压入栈S2中,因为操作数在后缀表达式中不需要进行优先级比较。
步骤4:遇到运算符时,比较其与S1栈顶运算符的优先级:
**原因:**当我们遇到一个运算符时,我们需要考虑它与栈S1中栈顶运算符的优先级,以确定是否需要对栈S1进行操作。
4.1 如果S1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
原因:如果栈S1为空,或者栈顶运算符是一个左括号"(",那么我们可以直接将当前运算符压入栈S1。
4.2 否则,若优先级比栈顶运算符的高,也将运算符压入S1;
原因:如果当前运算符的优先级比栈顶运算符的优先级高,我们可以将当前运算符直接压入栈S1。
4.3 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
原因:如果当前运算符的优先级低于或等于栈顶运算符的优先级,我们需要将栈S1栈顶的运算符弹出,并将其压入栈S2中。然后我们再次进行与新的栈顶运算符的比较。
步骤5:遇到括号时:
**原因:**当我们遇到一个括号时,需要特殊处理它们,因为它们对于确定运算符的优先级和结合性至关重要。
5-1:如果是左括号“(”则直接压入S1;
原因:如果遇到一个左括号"(",我们可以直接将其压入栈S1。
5-2:如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃;
原因:如果遇到一个右括号")“,我们需要依次从栈S1中弹出运算符,并将它们压入栈S2中,直到遇到左括号”("为止。然后我们将这一对括号丢弃。
步骤6:重复步骤(2)至(5),直到表达式的最右边;
**原因:**我们继续重复步骤2至步骤5,处理表达式中剩余的字符,直到我们扫描完整个中缀表达式。
步骤7:将S1中剩余的运算符依次弹出并压入S2;
原因:在完成了对中缀表达式的扫描后,如果栈S1中还有剩余的运算符,我们需要将它们依次弹出,并将它们压入栈S2中。
步骤8:依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式;
原因:最后,我们从栈S2中依次弹出元素并输出,得到的顺序即为中缀表达式对应的后缀表达式。我们需要注意到,输出结果的顺序与中缀表达式中的顺序相反,这是因为我们使用栈的先入后出特性。
代码
java code
import java.util.Stack;
public class Example {
public static void main(String[] args) {
String infixExpression = "3 + 4 * 5 + 6 / 2";
// 移除空格
infixExpression = infixExpression.replaceAll("\\s+", "");
// 创建操作数栈和运算符栈
Stack<Integer> operandStack = new Stack<>();
Stack<Character> operatorStack = new Stack<>();
// 遍历中缀表达式
for (int i = 0; i < infixExpression.length(); i++) {
char ch = infixExpression.charAt(i);
// 处理操作数
if (Character.isDigit(ch)) {
int operand = ch - '0';
operandStack.push(operand);
}
// 处理运算符
else {
while (!operatorStack.isEmpty() && getPrecedence(operatorStack.peek()) >= getPrecedence(ch)) {
int operand2 = operandStack.pop();
int operand1 = operandStack.pop();
char operator = operatorStack.pop();
int result = performOperation(operator, operand1, operand2);
operandStack.push(result);
}
operatorStack.push(ch);
}
}
// 执行剩余的操作
while (!operatorStack.isEmpty()) {
int operand2 = operandStack.pop();
int operand1 = operandStack.pop();
char operator = operatorStack.pop();
int result = performOperation(operator, operand1, operand2);
operandStack.push(result);
}
// 最终结果
int finalResult = operandStack.pop();
System.out.println("计算结果:" + finalResult);
}
// 获取运算符优先级
private static int getPrecedence(char operator) {
switch (operator) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return -1;
}
}
// 执行运算
private static int performOperation(char operator, int operand1, int operand2) {
switch (operator) {
case '+':
return operand1 + operand2;
case '-':
return operand1 - operand2;
case '*':
return operand1 * operand2;
case '/':
return operand1 / operand2;
default:
return 0;
}
}
}
python code
class Example:
@staticmethod
def main(args):
infixExpression = "3 + 4 * 5 + 6 / 2"
# 移除空格
infixExpression = infixExpression.replace(" ", "")
# 创建操作数栈和运算符栈
operandStack = []
operatorStack = []
# 遍历中缀表达式
for i in range(len(infixExpression)):
ch = infixExpression[i]
# 处理操作数
if ch.isdigit():
operand = int(ch)
operandStack.append(operand)
# 处理运算符
else:
while operatorStack and Example.getPrecedence(operatorStack[-1]) >= Example.getPrecedence(ch):
operand2 = operandStack.pop()
operand1 = operandStack.pop()
operator = operatorStack.pop()
result = Example.performOperation(operator, operand1, operand2)
operandStack.append(result)
operatorStack.append(ch)
# 执行剩余的操作
while operatorStack:
operand2 = operandStack.pop()
operand1 = operandStack.pop()
operator = operatorStack.pop()
result = Example.performOperation(operator, operand1, operand2)
operandStack.append(result)
# 最终结果
finalResult = operandStack.pop()
print("计算结果:", finalResult)
# 获取运算符优先级
@staticmethod
def getPrecedence(operator):
if operator in ('+', '-'):
return 1
elif operator in ('*', '/'):
return 2
else:
return -1
# 执行运算
@staticmethod
def performOperation(operator, operand1, operand2):
if operator == '+':
return operand1 + operand2
elif operator == '-':
return operand1 - operand2
elif operator == '*':
return operand1 * operand2
elif operator == '/':
return operand1 / operand2
else:
return 0
Example.main(None)
参考资料
https://zhuanlan.zhihu.com/p/37467928