如何用括号验证中缀表达式?

时间:2015-09-18 16:35:35

标签: java algorithm

我正在努力解决一个问题,即要求将中缀表达式解析为postfix。但首先我需要检查中缀表达式是否正确。怎么做?

在输入中,有效:

  • 操作数:所有字母都是大写和小写(' a' ...''' A' ...' Z')和数字(0 ... 9)。
  • 括号。
  • 运算符:接受以下优先级表中的所有后续运算符:

Table of operators

我看到的一个可能的解决方案是首先从中缀解析为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!");
            }
        }
    }
}

1 个答案:

答案 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)三元组周围有括号。从最外面的操作中删除括号,然后你就可以了。