我正在努力解决一个问题,即要求将中缀表达式解析为postfix。但首先我需要检查中缀表达式是否正确。怎么做?
在输入中,有效:
我看到的一个可能的解决方案是首先从中缀解析为postfix并检查后缀表达式是否有效。
我尝试将以下算法用于解析infix-postfix
1)从左到右读取表达式中的每个字符并创建一个空堆栈
2)如果当前字符是输出上的操作数
3)如果当前字符是运算符且堆栈为空;把它推到堆叠 4)如果当前字符是运算符且堆栈不为空;当堆栈顶部的运算符具有更高的优先级时,当前运算符将堆栈顶部放在输出上并弹出它;最后推动堆叠当前字符;
5)如果当前字符是'('将其推入堆栈 6)如果当前角色是')'弹出堆栈并输出;直到找到一个'('。7)输入结束时;如果堆栈不为空;弹出所有元素并将其放在输出上。
但是这种算法在以下情况下不起作用:"(ab + c)/ 2 (e + a)"。因为它将构造一个有效的后缀表达式。但是这个表达式不是有效的中缀表达式。
这是我的代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Stack;
public class Main {
public static boolean isOperand(char c) {
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
public static int getThePrecedence(char c){
if(c == '^')
return 6;
if(c == '*' || c == '/')
return 5;
if(c == '-' || c == '+')
return 4;
if(c == '>' || c == '<' || c == '=' || c == '#')
return 3;
if(c == '.')
return 2;
if(c == '|')
return 1;
return 0;
}
public static boolean checkParenthesis(String s){
int stack = 0;
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
if(c == '(')
++stack;
else if(c == ')'){
--stack;
if(stack < 0)
return false;
}
}
return stack == 0;
}
public static String parseInfixToPostfix(String s) {
StringBuilder out = new StringBuilder("");
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
if (isOperand(c)) {
out.append(c);
continue;
}
if (c == '(') {
stack.add(c);
continue;
}
if (c == ')') {
char ch = stack.pop();
//create a temporary stringbuilder to check if the expression has an empty parenthesis ()
StringBuilder temp = new StringBuilder("");
while (ch != '(') {
temp.append(ch);
ch = stack.pop();
}
if(temp.length() == 0) //empty parenthesis
return ""; //will be invalidated by the postfix checker
out.append(temp.toString());
continue;
}
//here are only operators
if(stack.empty()){
stack.push(c);
}
else{
while(!stack.empty() && ( getThePrecedence(stack.peek()) >= getThePrecedence(c) ))
out.append(stack.pop());
stack.push(c);
}
}
while(!stack.empty())
out.append(stack.pop());
return out.toString();
}
public static boolean postFixValidate(String s){
if(s.equals(""))
return false;
int stack = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(c == '(' || c == ')')
continue;
if(isOperand(c)){
++stack;
}
else {
stack -= 2;
if(stack < 0)
return false;
++stack;
}
}
return stack == 1;
}
public static boolean lexicalAnalysis(String s){
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(isOperand(c) == false && getThePrecedence(c) == 0 && c != '(' && c != ')')
return false;
}
return true;
}
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
PrintStream out = new PrintStream(System.out);
while (true) {
String s = in.readLine();
if (lexicalAnalysis(s)) {
if(checkParenthesis(s)){
String postfix = parseInfixToPostfix(s);
if(postFixValidate(postfix)){
System.out.println(postfix);
}
else{
out.println("Syntax Error!");
}
}
else{
out.println("Syntax Error!");
}
} else {
out.println("Lexical Error!");
}
}
}
}
答案 0 :(得分:1)
这样做的方法是构建一个解析树。节点将是运营商,叶子将是终端符号(数字或字母)。如果你遇到的情况是你试图把孩子放在终点符号下,或者某些操作数没有两个孩子,你就完成了。考虑“(ab + c)/ 2(e + a)”。我们正在解析中缀,所以我们知道我们将首先阅读的是某些操作的LHS。我们看到左括号,所以我们递归。我们知道我们将看到的第一件事是某些操作的LHS,我们看到'a'。我们现在期望看到一个运算符,但是看到'b',这是一个错误,因为'a'不能是'b'的左子(因为它们都是终结符号)。这出错了。
假设'b'不存在,我们会看到'+'。那么一切都很顺利,接下来我们期待RHS。我们看到'c',一切都很好,然后右括号提示我们结束那里的递归。我们的树看起来像:
?
/ \
add ?
/ \
a b
看过LHS后,我们现在看到'/'。都好。我们的树现在
divide
/ \
add ?
/ \
a b
接下来我们看到一个2,一切都很好,但后来我们已经看到了我们所需要的一切,并且期望什么也看不到。看到右括号会让我们明白构建树是失败的。
假设2没有出现。我们看到左括号并递归。我们将'e'视为LHS,'+'视为运算符,'a'视为RHS,然后是右括号,结束递归。我们的树现在看起来像
divide
/ \
add add
/ \ / \
a b e a
这是一个有效的树,我们可以通过对树的前序遍历来获取后缀表达式:我们得到'/ + ab + ea'。基本算法前序遍历是:
preorder(node)
print node.value
preorder(node.left)
preorder(node.right)
请注意,到目前为止,我还没有解决答案中的操作顺序。您可以通过首先完全括号化中缀表达式来完全避免此问题;这简化了其余部分。基本上,只需按照您喜欢的顺序开始查找运算符,然后确保(LHS op RHS)三元组周围有括号。从最外面的操作中删除括号,然后你就可以了。