【问题标题】:how to validate infix expressions with parenthesis?如何用括号验证中缀表达式?
【发布时间】:2015-12-15 21:30:11
【问题描述】:

我遇到了一个要求将中缀表达式解析为后缀的问题。但首先我需要检查中缀表达式是否正确。该怎么做?

在输入中,都是有效的:

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

我见过的一个可能的解决方案是先从中缀解析到后缀,然后检查后缀表达式是否有效。

我尝试使用以下算法解析中缀后缀

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!");
            }
        }
    }
}

【问题讨论】:

  • 也许你最好使用编译器-编译器来解析Yaccbison之类的表达式。

标签: java algorithm


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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-15
    • 2017-07-20
    • 2013-11-12
    • 2016-06-18
    • 2014-10-19
    • 1970-01-01
    • 2014-05-10
    • 1970-01-01
    相关资源
    最近更新 更多