前缀/中缀/后缀----表达式之间的相互转换_中缀表达式转前缀表达式-CSDN博客
一.基本定义
1.前缀
前缀表达式,指的是不包含括号,运算符放在两个运算对象的前面,严格从右向左进行(不再考虑运算符的优先规则),所有的计算按运算符出现的顺序。
eg:
例如:中缀表达式 ( 2 + 3 ) × 4 - 5
,采用前缀表达式为:- × + 2 3 4 5
13 * ((3 + 8) * 4),前缀表达式为 * 13 * + 3 8 4
2.后缀
同理
二:转换
1.中缀转换成后缀
#include<iostream>
#include<stack>
using namespace std;
bool isOperator(char c)
{
if (c == '+' || c == '-' || c == '*' || c == '/')
return 1;
else return 0;
}
int Priority(char c)
{
switch (c)
{
case '(':return 3; /注意这里数字越大优先级越低,(的优先级默认最低,可能跟常识不一样,仔细思考为什么,就会不在细讲了
case '*':
case '/':return 1;
case '+':
case '-':return 2;
}
}
int main()
{
int num = 0;
char arr[100];
char c;
stack<char> op;//创建一个栈op
while (1)//使程序一直循环,可以多次实现转换的功能
{
cin.getline(arr, 100);
int i = 0;
int len = (int)strlen(arr);//strlen返回的unsign int类型,需要强制转换为int
while (i < len)//挨个处理字符
{
//首先是判断这个字符是数字还是括号还是运算符
if (isdigit(arr[i]))//判断字符是否为数字,在库函数里,不用自己定义
{
num = 0;
do
{
num = num * 10 + (arr[i] - '0');//num*10是存在多位数时使用 例如 38+90
i++;
} while (isdigit(arr[i]));
cout << num ;
}
else if (arr[i] == '(')
{
op.push(arr[i]);
i++;
}
else if (isOperator(arr[i]))
{
if (op.empty())//判断栈是不是空
{
//栈空,直接压栈
op.push(arr[i]);
i++;
}
// 比较栈op顶的操作符与ch的优先级
// 如果ch的优先级高,则直接压入栈
// 否则,推出栈中的操作符,直到操作符小于ch的优先级,或者遇到(,或者栈已空
else//不空,判断优先级
{
while (!op.empty())//需要保证栈不能为空,因为栈为空的时候在进行比较等操作会出错,并且有可能需要弹出多个字符,需要while循环
{
c = op.top();//将栈顶的元素拷贝给c
if (Priority(arr[i]) < Priority(c))//arr[i]的优先级更大,直接压栈
{
//这里不需要op.push(arr[i]);i++;因为这是在循环里,可能会把同一个字符多次压栈,所以放在循环外面
break;
}
else
{
cout << c;
op.pop();
}
}
op.push(arr[i]);
i++;
}
}
else if (arr[i] == ')')
{
while (op.top() != '(')
{
//将栈中的运算符全部弹出,直到遇到‘(’
cout << op.top();
op.pop();
}
op.pop();
i++;
}
else//如果是空字符,就直接处理下一个字符
{
i++;
}
}
while (!op.empty()) //不要忘记栈中剩余的符号都弹出来
{
cout << op.top();
op.pop();
}
cout << endl;
}
}
遇到的问题或者错误:
1.关于符号的优先度
潜意识的就认为括号优先度最大,所以在求优先度的函数中把括号的优先度设成最大。这样是错误的!因为严格来说括号时不应该放在这个优先度比较的函数中的,只是因为某些原因(方便)
因为如果把括号的优先度调成最大的话:
看下面这种情况:
栈中(从上到下):( + * -
此时对于正在处理的字符来说:如果(的优先度最大,那么结果会把(弹出并打印出来,这样显然是不对的
2.关于while()循环
else//不空,判断优先级
{
while (!op.empty())//需要保证栈不能为空,因为栈为空的时候在进行比较等操作会出错,并且有可能需要弹出多个字符,需要while循环
{
c = op.top();//将栈顶的元素拷贝给c
if (Priority(arr[i]) < Priority(c))//arr[i]的优先级更大,直接压栈
{
//这里不需要op.push(arr[i]);i++;因为这是在循环里,可能会把同一个字符多次压栈,所以放在循环外面
break;
}
else
{
cout << c;
op.pop();
}
}
op.push(arr[i]);
i++;
}
上面这一部分 的while循环,如果没有的话判断的每一个运算符只能弹出一个比他优先级高的运算符,弹出一个后新的栈顶元素的优先级依然大于这个运算符,这个时候也要把他弹出,所以要使用循环
3.最后一个while循环
负责把剩余的栈中的元素弹出来
拿最简单的例子:
8 + 4
如果没有这个循环,那么转换后结果:
84
栈的知识巩固:
#include <stack>
stack <int> a;
stack <char> b;
//常用的函数
a.push(9);
b.push('*');
a.pop();
a.empty();
2.中缀转换成前缀
和后缀核心思想一样,就只是输出的顺序变了,输出的时候也类似一个栈
3.前缀转换成后缀
就是把前缀换成中缀在换成后缀
在数据结构的习题中有这一类题:
已知前缀和后缀的序列,画出树(不一定是二叉树)
先根:GFKDAIEBCHJ
后跟:DIAEKFCJHBG
这些是我自己猜测加总结:不一定准确
①确定根,即先根的第一个字母,后跟倒一个字母
②确定左子树:先根的第二个字母
③确定右子树:后跟的倒数第二个字母
④观察左子树再后跟中的位置,则它左面的都是根的左边,它的右面都是跟的右面
以此类推