中缀表达式转后缀表达式
(本文为笔者个人学习笔记,如有不当之处恳请各位读者指正)
在一些简单的加减乘除算式中我们可以很容易的看出哪个运算符要先进行计算,但对于计算机来说却并不容易。为了能让计算机能够准确的计算出结果,我们需要将算式进行简单的转换,将优先级高的运算符排在前面,即将中缀表达式转化为后缀表达式(逆波兰式)。
例如给出前缀表达式:1+2*(4-3),很明显在这个表达式中运算符优先级由高到底的排列顺序为:-、*、+,转换为后缀表达式即:1243-*+
优先级:乘除的优先级高于加减,数字与操作符优先级相同,左括号的优先级低于所有运算符和数字(不需要对右括号的优先级定义,因为碰到右括号就直接开始弹栈顶)。
转换过程:
(1)初始化一个栈,和一个后缀表达式数组
(2)从左到右依次读取前缀表达式的每个字符
- 遇到左括号直接进栈。
- 遇到右括号依次弹出栈顶元素,直至将左括号弹出,即弹出左右括号括起来的内容,并记录在后缀表达式数组中。
- 遇到数字直接进栈。
- 遇到运算符(该运算符还未进栈):
p:若栈为空或栈顶元素(即表达式的上一个字符)的优先级低于当前遇到的操符, 该运算符直接进栈。
q:若栈不为空且栈顶元素的优先级高于或等于当前遇到的操作符,依次弹出栈顶 元素,并 记录在后缀表达式数组中直至栈 为空或栈顶元素的优先级低于当前遇 到的操作 符.最后将该操作符压入栈 。
- 依次弹出栈顶元素直至栈空
例:1+2*((4-3)+6/2),依次读取每个字符
char c='1';
数字直接进栈:
1 |
char c='+';
栈不为空,且栈顶元素为数字(条件q),数字的优先级与操作符相同,所以弹出栈顶元素并记录入后缀数组(后缀数组:1)
此时栈内情况:
此将该字符压入栈:
+ |
char c='2';
数字直接进栈:
2 |
+ |
char c='*';
栈不为空,且栈顶元素为数字(条件q),数字的优先级与操作符相同,所以弹出栈顶元素并记录入后缀数组(后缀数组:12)
此时栈内情况:
+ |
加号优先级低于乘号(条件p),乘号进栈:
* |
+ |
char c='(';
左括号直接进栈:
( |
* |
+ |
char c='4';
数字直接进栈:
4 |
( |
* |
+ |
char c='-;
栈不为空,且栈顶元素为数字(条件q),数字的优先级与操作符相同,所以弹出栈顶元素并记录入后缀数组(后缀数组:124),并将"-"号进栈。
此时栈内情况:
- |
( |
* |
+ |
char c='3';
数字直接进栈:
3 |
- |
( |
* |
+ |
char c=')';
开始依次弹出栈顶元素并记录弹出的元素,直至弹出第一个左括号(后缀数组:1243-):
* |
+ |
此时表达式读完,开始依次弹出栈顶元素并将弹出的元素记录到后缀数组中,直至栈空:
后缀数组1243-*+
//中缀表达式infix转后缀表达式postfix
private static void infixtopostfix(char[] infix, char[] postfix) {
index=0;
// LinkedList对象以push形式添加元素时可做堆栈用,以add形式添加元素时可做队列用
LinkedList<Character> st=new LinkedList();
for(int i=0;i<infix.length;i++){
char c=infix[i]; // 依次读取中缀数组中的各个字符
if(c=='(')
st.push(c);
else if(c==')'){
while(st.peek()!='('){//记录下左右括号内的操作符。peek():仅获取栈顶元素而不弹出
postfix[index++]=st.pop();;
}
st.pop();//弹出左括号
}else{
if(c>='0'&&c<='9') // 数字直接进栈
st.push(c);
else{
while(!st.isEmpty()&&precedence(st.peek(),c)>=0){//栈不为空&&上一个操作符的优先级高于当前操作符c
postfix[index++]=st.pop();
}
st.push(c);//当前操作符一定要进栈,并在下一层循环或最后弹出
}
}
}
//弹出栈中剩余的操作符
while(!st.isEmpty()){
postfix[index++]=st.pop();
}
}
比较两个符号的优先级:
/*
* 比较两个操作符的优先级,
* post:上一个操作符. c:当前的操作符
* 返回1:上一个操作符的优先级高于当前操作符c的优先级,返回0:两个操作符的优先级相同,返回-1:上一操作符的优先级低于当前操作符c
*/
private static int precedence(char post, char c) {
if(post=='(')
return -1;//上一个操作符为'(',返回-1,操作符进栈
if(post=='+'||post=='-'){
if(c=='*'||c=='/')
return -1;
else
return 0;
}else if(post=='*'||post=='/'){
if(c=='+'||c=='-')
return 1;
else
return 0;
}
return 0; // 栈顶元素为数字,操作数与操作符比较返回0
}
完整代码:
import java.util.*;
public class test {
private static int index;
public static void main(String[] args) {
System.out.println("请输入中缀表达式:");
Scanner sc=new Scanner(System.in);
char[] infix=sc.nextLine().toCharArray();
char[] postfix=new char[infix.length];
infixtopostfix(infix,postfix);
System.out.println("后缀表达式为:");
for(int i=0;i<index;i++){
System.out.print(postfix[i]);
}
}
//中缀表达式infix转后缀表达式postfix
private static void infixtopostfix(char[] infix, char[] postfix) {
index=0;
// LinkedList对象以push形式添加元素时可做堆栈用,以add形式添加元素时可做队列用
LinkedList<Character> st=new LinkedList();
for(int i=0;i<infix.length;i++){
char c=infix[i]; // 依次读取中缀数组中的各个字符
if(c=='(')
st.push(c);
else if(c==')'){
while(st.peek()!='('){//记录下左右括号内的操作符。peek():仅获取栈顶元素而不弹出
postfix[index++]=st.pop();;
}
st.pop();//弹出左括号
}else{
if(c>='0'&&c<='9') // 数字直接进栈
st.push(c);
else{
while(!st.isEmpty()&&precedence(st.peek(),c)>=0){//栈不为空&&上一个操作符的优先级高于当前操作符c
postfix[index++]=st.pop();
}
st.push(c);//当前操作符一定要进栈,并在下一层循环或最后弹出
}
}
}
//弹出栈中剩余的操作符
while(!st.isEmpty()){
postfix[index++]=st.pop();
}
}
/*
* 比较两个操作符的优先级,
* 返回1:上一个操作符的优先级高于当前操作符c的优先级,返回0:两个操作符的优先级相同,返回-1:上一操作符的优先级低于当前操作符c
*/
private static int precedence(char post, char c) {
if(post=='(')
return -1;//上一个操作符为')',返回-1,操作符进栈
if(post=='+'||post=='-'){
if(c=='*'||c=='/')
return -1;
else
return 0;
}else if(post=='*'||post=='/'){
if(c=='+'||c=='-')
return 1;
else
return 0;
}
return 0; // 栈顶元素为数字,操作数与操作符比较返回0
}
}