分别讨论中缀表达式、后缀表达式和前缀表达式的概念和特点,以及如何利用栈的特性来实现它们之间的相互转换和求值。我们还将给出相应的算法和代码实现,并分析它们的时间复杂度和空间复杂度。
中缀表达式是我们日常使用的最常见的表达式形式,它是按照运算符的优先级和括号来确定运算顺序的。但是,对于计算机来说,中缀表达式并不方便处理,因为它需要不断地扫描和判断运算符的优先级,以及处理括号的匹配问题。因此,我们可以将中缀表达式转换为后缀表达式或前缀表达式,这两种形式都可以避免使用括号,并且运算符的出现顺序就是运算顺序,从而简化了计算过程。
后缀表达式又称为逆波兰表达式,它是将运算符放在操作数的后面,例如a+b*c可以写成abc*+。后缀表达式的计算方法是从左到右扫描,每遇到一个运算符,就从栈顶弹出两个操作数进行运算,并将结果压回栈顶。最终栈顶元素就是整个表达式的值。为了将中缀表达式转换为后缀表达式,我们可以使用一个栈来存储暂时不确定生效顺序的运算符,遵循左优先原则,即如果左边的运算符能先运算,就先运算左边的。具体来说,我们从左到右扫描中缀表达式,如果遇到操作数,直接加入后缀表达式;如果遇到左括号,入栈;如果遇到右括号,则依次弹出栈内元素并加入后缀表达式,直到遇到左括号为止;如果遇到其他运算符,则比较其与栈顶元素的优先级,如果低于或等于栈顶元素,则弹出栈顶元素并加入后缀表达式,重复此过程直到栈为空或者遇到更高优先级的运算符为止,然后将当前运算符入栈。最后扫描完中缀表达式后,将栈内剩余元素依次弹出并加入后缀表达式。
前缀表达式又称为波兰表达式,它是将运算符放在操作数的前面,例如a+b*c可以写成+*abc。前缀表达式的计算方法是从右到左扫描,每遇到一个运算符,就从栈顶弹出两个操作数进行运算,并将结果压回栈顶。最终栈顶元素就是整个表达式的值。为了将中缀表达式转换为前缀表达式,我们可以使用一个栈来存储暂时不确定生效顺序的运算符,遵循右优先原则,即如果右边的运算符能先运算,就先运算右边的。具体来说,我们从右到左扫描中缀表达式,如果遇到操作数,直接加入前缀表达式的前面;如果遇到右括号,入栈;如果遇到左括号,则依次弹出栈内元素并加入前缀表达式的前面,直到遇到右括号为止;如果遇到其他运算符,则比较其与栈顶元素的优先级,如果低于或等于栈顶元素,则弹出栈顶元素并加入前缀表达式的前面,重复此过程直到栈为空或者遇到更高优先级的运算符为止,然后将当前运算符入栈。最后扫描完中缀表达式后,将栈内剩余元素依次弹出并加入前缀表达式的前面。
通过上述方法,我们可以实现中缀表达式、后缀表达式和前缀表达式之间的相互转换和求值。这些方法的时间复杂度都是O(n),空间复杂度都是O(n),其中n是表达式的长度。在实际编程中,我们需要注意一些细节,例如如何处理多位数、小数、负数等。下面给出了使用C语言实现的部分代码示例。
一、手算
1.中缀表达式转后缀(手算)
左优先原则:若左边的运算符能先运算,就先运算左边的
用左优先原则得到的后缀表达式,运算符出现的顺序正好是原中缀表达式中运算符的运算顺序。
(好耶)
手算后缀表达式:从左往右扫,每扫到一个运算符,就把离得最近(栈顶)的两个操作数拿出来运算,并把结果压入栈顶。
注意:两个操作数の顺序是左边的先,右边的后,先出栈的是右操作数。
2.后缀表达式转成中缀(手算)
从左往右扫,遇到操作符,取出最近两个操作数运算,和基于栈计算后缀表达式一样。
3.中缀表达式转前缀表达式(手算)
中缀表达式转前缀
右优先原则:从右往左扫,只要靠右的运算符能先运算,就先算右边的。
运算符的运算顺序与前缀表达式中从右到左运算符出现的顺序相同。
注意:是从左往右扫描后缀表达式,是从右往左扫描前缀表达式;后缀表达式机算时先弹出的操作数是右操作数;前缀表达式机算时先弹出的栈顶元素是左操作数
二、机算实现中缀表达式の求值
中缀表达式计算=中缀转后缀+后缀表达式计算。对于计算机来说,后缀表达式比中缀好算。
注意一些区别:
(1)在中缀转后缀中,栈存放暂时不确定生效顺序の运算符;在后缀表达式の计算中,栈用来存放暂时不确定运算顺序の操作数。
(2)中缀转后缀时,运用左优先原则,左优先原则下,先弹出的是右操作数。
1.中缀表达式转后缀(用栈实现)
初始化两个栈:操作数栈&运算符栈。
定义一个判断是否是运算符的函数,判断加减乘除。
定义一个判断运算符优先级的函数,加减比乘除低。括号的优先级比其他运算符高,至于要不要在改函数里也进行判断,可根据喜好而定。
// 栈的基本操作
typedef struct Stack {
char* data;
int top;
int size;
} Stack;
Stack* createStack(int size) {
Stack* stack = (Stack*)malloc(sizeof(Stack));
stack->data = (char*)malloc(size * sizeof(char));
stack->top = -1;
stack->size = size; // 栈的最大容量
return stack;
}
void push(Stack* stack, char c) {
if (stack->top == stack->size - 1) {
printf("Stack overflow\n");
return;
}
stack->data[++stack->top] = c;
}
char pop(Stack* stack) {
if (stack->top == -1) {
printf("Stack underflow\n");
return '\0';
}
return stack->data[stack->top--];
}
char peek(Stack* stack) { // 返回栈顶元素
if (stack->top == -1) {
printf("Stack underflow\n");
return '\0'; // 空字符
}
return stack->data[stack->top];
}
int isEmpty(Stack* stack) {
return stack->top == -1;
}
// 中缀转后缀
int isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
int precedence(char c) { // 优先级
if (c == '+' || c == '-')
return 1;
if (c == '*' || c == '/')
return 2;
return 0;
}
void infixToPostfix(char* infix, char* postfix) { // 中缀转后缀,infix中缀表达式,postfix后缀表达式
Stack* stack = createStack(strlen(infix));
int i, j;
for (i = 0, j = 0; infix[i]; ++i) { // 遍历中缀表达式
char c = infix[i]; // 存储当前字符
if (isOperator(c)) { // 如果是运算符
while (!isEmpty(stack) && precedence(peek(stack)) >= precedence(c)) // 栈不为空,且栈顶元素优先级大于等于当前运算符
postfix[j++] = pop(stack); // 栈顶元素出栈,加入后缀表达式
push(stack, c); // 当前运算符入栈
}
else if (c == '(') { // 如果是左括号,直接入栈
push(stack, c);
}
else if (c == ')') { // 如果是右括号
while (!isEmpty(stack) && peek(stack) != '(') // 栈不为空,且栈顶元素不是左括号
postfix[j++] = pop(stack); // 栈顶元素出栈,加入后缀表达式
pop(stack); // 左括号出栈
}
else { // 如果是操作数
postfix[j++] = c; // 直接加入后缀表达式
}
}
while (!isEmpty(stack)) // 栈不为空
postfix[j++] = pop(stack); // 栈顶元素出栈,加入后缀表达式
postfix[j] = '\0';
}
定义一个函数实现中缀转后缀:
创建一个栈用于存储运算符,遍历中缀表达式,如果扫描到当前字符是运算符,有两种情况:如果当前运算符的优先级低于或等于栈顶元素,根据左优先原则,则栈顶元素出栈,加入后缀表达式,最终当前运算符入栈;如果优先级更高,则入栈。
如果扫描到的是左括号,入栈(相当于优先级最高的运算符)。
如果扫描到的是右括号(相当于优先级最低的运算符),在安全的前提下(栈非空),所有元素出栈加入后缀表达式,然后左括号出栈(不要了)。
如果扫描到的是操作数,直接加入后缀表达式。
不要忘记!遍历完中缀表达式后,栈内剩余元素出栈,加入后缀表达式。
2.后缀表达式の计算
运算符栈每弹出一个运算符(运算符的弹出即为生效),从操作数栈栈顶弹出两个操作数(先弹出的是右操作数op2),运算结果再作为为操作数压回栈顶。
// 后缀表达式的求值
int evaluatePostfix(char* postfix) {
Stack* stack = createStack(strlen(postfix));
int i, op1, op2;
for (i = 0; postfix[i]; ++i) { // 遍历后缀表达式
char c = postfix[i]; // 存储当前字符
if (isdigit(c)) // 如果是操作数
push(stack,c - 0); // 将操作数入栈, c - 0将字符转换为数字
else { // 如果是运算符
op2 = pop(stack); // 第二个操作数
op1 = pop(stack); // 第一个操作数
switch (c) { // 根据运算符进行运算
case '+': push(stack, op1 + op2); break;
case '-': push(stack, op1 - op2); break;
case '*': push(stack, op1 * op2); break;
case '/': push(stack, op1 / op2); break;
}
}
}
return pop(stack); // 返回栈顶元素,即为最终结果
}
int main() {
char infix[100], postfix[100];
printf("Enter infix expression: ");
cin>>infix;
infixToPostfix(infix, postfix);
printf("Postfix expression: %s\n", postfix);
printf("Result: %d\n", evaluatePostfix(postfix));
return 0;
}