问题描述
算术表达式有前缀表示法、中缀表示法和后缀表示法等形式。日常使用的算术表达式是采用中缀表示法,即二元运算符位于两个运算数中间。请设计程序将中缀表达式转换为后缀表达式。
输入格式:
输入在一行中给出不含空格的中缀表达式,可包含+
、-
、*
、/
以及左右括号()
,表达式不超过20个字符。
输出格式:
在一行中输出转换后的后缀表达式,要求不同对象(运算数、运算符号)之间以空格分隔,但结尾不得有多余空格。
样例">样例">样例">样例">样例">样例">输入样例:
2+3*(7-4)+8/4
输出样例:
2 3 7 4 - * + 8 4 / +
重要步骤分析:
1、输入和输出
- 输入是字符串;
- 输出需要划分操作数和操作符,中间用空格分隔,设计ArrayList类型对象result,用于保存不同的运算数和运算符,方便将来输出时加入空格分隔符。(也可以用队列)
2、运算数的处理
- 运算数可能存在的形式有1位数、多位数、整数、实数、正数、负数。
- 运算数可能是多位数,可以用StringBuffer保存,找出完整的一个运算数后,再将它转成字符串存入result中。
- 多位数的特点是连续的数字;
- 实数的特点是数字和小数点连接在一起;
- 正负数的判断稍复杂一点,但可以找出它们出现的规律,是第1个符号或出现在左括号的后边。例:-2*(+3)
3、运算符的处理
- 运算符要根据优先级调整次序,这里用到栈。
- 如果当前运算符之前 没有运算符(栈空),直接入栈; 如果之前有运算符(栈不空),取前一个运算符(栈顶)与当前运算符比较优先级:
- 栈顶优先级高时,取出栈顶,记入reslut中;重复当前运算符与栈顶的比较;
- 当前运算符优先级高时,当前运算符入栈,继续读取下一个符号;
- 栈顶是左括号(,当前运算符是右括号)时,可以视为优先级相同,栈顶出栈(不记入reslut中),跳过当前运算符;(丢掉左右括号)
- 处理完所有符号后,记得将栈里的运算符逐个出栈,加入result中;
- **关于运算符的优先级比较,可以设置优先级矩阵,如下表:
其中:1表示优先级高于,-1表示 优先级低于,0表示相等,-2表示异常
比较两个运算符优先级的算法如下图:
代码实现如下:(Java)
import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static boolean isNumber(char ch) {// 判断字符是否数字
if (ch >= '0' && ch <= '9')
return true;
else
return false;
}
public static boolean isPoint(char ch) {// 判断是否小数点
if (ch == '.')
return true;
else
return false;
}
public static int priority(char op1, char op2) {
// 比较op1,op2的优先级
// op1>op2,返回1
// op1<op2,返回-1
// 左右括号( ),返回0
// 异常返回-2
char[] op = { '+', '-', '*', '/', '(', ')' };
int[][] priority = {
{ 1, 1, -1, -1, -1, 1 },
{ 1, 1, -1, -1, -1, 1 },
{ 1, 1, 1, 1, -1, 1 },
{ 1, 1, 1, 1, -1, 1 },
{ -1, -1, -1, -1, -1, 0 },
{ -1, -1, -1, -1, -2, 1 }
};
int i, j;
for (i = 0; i < op.length; i++)
if (op1 == op[i])
break;
for (j = 0; j < op.length; j++)
if (op2 == op[j])
break;
return priority[i][j];
}
public static void expressionConversion(String str) {
// str表示的中缀表达式转换成后缀表达式输出
ArrayList<String> result = new ArrayList<String>(); //存放后缀表达式
Stack<Character> stack = new Stack<Character>(); //调整运算符用到的栈
StringBuffer data = new StringBuffer(); // 用于保存多位整数、实数、负数等
for (int i = 0; i < str.length(); ) {
char ch = str.charAt(i);
if ((ch =='+' || ch =='-') && (i ==0 || i > 0 && str.charAt(i-1) == '(')) {
// 正负数处理,正负号可能出现 在第1个符号位,或(后
// 如 -2*(+3)
// 其中负号要与数字连在一起,正号不输出
if (ch == '-')
data.append(ch);
i++;
continue;
}
if (isNumber(ch)) { // 遇到数字,要处理可能的连续多位数 或 实数
//从第1个数字起连续处理
//遇到非数字且非小数点、或超过字符串长度时结束
while (isNumber(ch) || isPoint(ch)) {
data.append(ch);
i++;
if (i < str.length())
ch = str.charAt(i);
else
break;
}
result.add(data.toString()); //运算数data存入result
data = new StringBuffer(); //data置空,为接收下一个运算数准备
} else {
// 处理运算符
// 栈空直接入栈,否则需要进行优先级比较,根据比较结果处理
if (stack.isEmpty()) {// 空栈,直接入栈运算符,读取下一个符号
stack.push(ch);
i++;
} else if (priority(stack.peek(), ch) == 1) {
// 栈顶优先级高,取出栈顶,放result中,
// 当前符号继续与栈顶符号比较,当前符号不变,继续循环
result.add(stack.pop() + "");
} else if (priority(stack.peek(), ch) == -1) {
// 栈顶优先级低,入栈,读取下一个符号
stack.push(ch);
i++;
} else if (priority(stack.peek(), ch) == 0) {
// 右括号遇到左括号,出栈,跳过右括号,继续读取下一个符号
stack.pop();
i++;
}
}
}
//栈中的运算符都记入result中
while (!stack.isEmpty()) {
result.add(stack.pop() + "");
}
//输出
int i;
for (i = 0; i < result.size() - 1; i++)
System.out.print(result.get(i) + " ");
System.out.println(result.get(i));
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
expressionConversion(str);
sc.close();
}
}
参考来源:数据结构之栈----PTA题目7-20表达式转换(中缀转后缀)_SiKongPop的博客-CSDN博客
致谢作者提供的测试数据样例,尤其是样例3!