使用堆栈从算术表达式中删除不必要/重复的括号

时间:2014-11-05 18:00:31

标签: java data-structures stack

编写一个程序,用于在表达式中查找重复的括号。 例如:

(( a + b ) + (( c + d ))) = a + b + c + d
(( a + b ) * (( c + d ))) = (a + b) * (c + d)

我所知道的一种方法涉及以下两个步骤:

  1. 将给定的中缀表达式转换为后缀表达式。
  2. 将后缀转换回中缀
  3. 我不想完成从一个表示转换为另一个表示的整个过程,然后将其转换回来。

    我想使用堆栈(但是一次性完成)。有可能吗?

    请建议算法或分享代码。

5 个答案:

答案 0 :(得分:5)

您可以使用recursive descent parser。这隐式使用函数调用堆栈,但不显式使用Java堆栈。它可以实现如下:

public class Main {

    public static void main(String[] args) {
        System.out.println(new Parser("(( a + b ) + (( c + d )))").parse());
        System.out.println(new Parser("(( a + b ) * (( c + d )))").parse());
    }
}

public class Parser {
    private final static char EOF = ';';
    private String input;
    private int currPos;

    public Parser(String input) {
        this.input = input + EOF; // mark the end
        this.currPos = -1;
    }

    public String parse() throws IllegalArgumentException {
        nextToken();
        Result result = expression();
        if(currToken() != EOF) {
            throw new IllegalArgumentException("Found unexpected character '" + currToken() + "' at position " + currPos);
        }
        return result.getText();
    }

    // "expression()" handles "term" or "term + term" or "term - term"
    private Result expression() throws IllegalArgumentException {
        Result leftArg = term();

        char operator = currToken();
        if (operator != '+' && operator != '-') {
            return leftArg; // EXIT
        }
        nextToken();

        Result rightArg = term();

        if(operator == '-' && (rightArg.getOp() == '-' || rightArg.getOp() == '+')) {
            rightArg = encloseInParentheses(rightArg);
        }

        return new Result(leftArg.getText() + " " + operator + " " + rightArg.getText(), operator);
    }

    // "term()" handles "factor" or "factor * factor" or "factor / factor"
    private Result term() throws IllegalArgumentException {
        Result leftArg = factor();

        char operator = currToken();
        if (operator != '*' && operator != '/') {
            return leftArg; // EXIT
        }
        nextToken();

        Result rightArg = factor();

        if(leftArg.getOp() == '+' || leftArg.getOp() == '-') {
            leftArg = encloseInParentheses(leftArg);
        }
        if(rightArg.getOp() == '+' || rightArg.getOp() == '-' || (operator == '/' && (rightArg.getOp() == '/' || rightArg.getOp() == '*'))) {
            rightArg = encloseInParentheses(rightArg);
        }

        return new Result(leftArg.getText() + " " + operator + " " + rightArg.getText(), operator);
    }

    // "factor()" handles a "paren" or a "variable"
    private Result factor() throws IllegalArgumentException {
        Result result;
        if(currToken() == '(') {
            result = paren();
        } else if(Character.isLetter(currToken())) {
            result = variable();
        } else {
            throw new IllegalArgumentException("Expected variable or '(', found '" + currToken() + "' at position " + currPos);
        }
        return result;
    }

    // "paren()" handles an "expression" enclosed in parentheses
    // Called with currToken an opening parenthesis
    private Result paren() throws IllegalArgumentException {
        nextToken();
        Result result = expression();
        if(currToken() != ')') {
            throw new IllegalArgumentException("Expected ')', found '" + currToken() + "' at position " + currPos);
        }
        nextToken();
        return result;
    }

    // "variable()" handles a variable
    // Called with currToken a variable
    private Result variable() throws IllegalArgumentException {
        Result result = new Result(Character.toString(currToken()), ' ');
        nextToken();
        return result;
    }

    private char currToken() {
        return input.charAt(currPos);
    }

    private void nextToken() {
        if(currPos >= input.length() - 1) {
            throw new IllegalArgumentException("Unexpected end of input");
        }
        do {
            ++currPos;
        }
        while(currToken() != EOF && currToken() == ' ');
    }

    private static Result encloseInParentheses(Result result) {
        return new Result("(" + result.getText() + ")", result.getOp());
    }

    private static class Result {
        private final String text;
        private final char op;

        private Result(String text, char op) {
            this.text = text;
            this.op = op;
        }

        public String getText() {
            return text;
        }

        public char getOp() {
            return op;
        }
    }
}

如果要使用显式堆栈,可以使用类似于Result内部类的堆栈将算法从递归堆栈转换为迭代堆栈。 实际上,Java编译器/ JVM将每个递归算法转换为基于堆栈的算法,将局部变量放到堆栈上。

但递归正常的解析器很容易被人类阅读,因此我更喜欢上面提到的解决方案。

答案 1 :(得分:3)

如果您只关心重复括号(因为问题似乎暗示),而不是由于运算符优先级而被视为必要的(正如其他答案似乎暗示的那样),您确实可以使用堆栈跟踪你遇到的括号,并确定每对括号的任何非空格非括号字符都很重要,这样就可以使用堆栈进行更简单的迭代遍历:

public class BracketFinder {

    public List<BracketPair> findUnnecessaryBrackets(String input) {
        List<BracketPair> unneccessaryBrackets = new LinkedList<BracketPair>();
        Deque<BracketPair> bracketStack = new LinkedBlockingDeque<BracketPair>();

        for (int cursor = 0; cursor < input.length(); cursor++ ) {
            if (input.charAt(cursor) == '(') {
                BracketPair pair = new BracketPair(cursor);
                bracketStack.addLast(pair);
            } else if (input.charAt(cursor) == ')') {
                BracketPair lastBracketPair = bracketStack.removeLast();
                lastBracketPair.end = cursor;
                if (!lastBracketPair.isNecessary) {
                    unneccessaryBrackets.add(lastBracketPair);
                }
            } else if (input.charAt(cursor) != ' ') {
                if (!bracketStack.isEmpty()) {
                    bracketStack.getLast().isNecessary = true;
                }
            }
        }

        return unneccessaryBrackets;
    }

    class BracketPair {
        public int start = -1;
        public int end = -1;
        public boolean isNecessary = false;

        public BracketPair(int startIndex) {
            this.start = startIndex;
        }
    }
}

您可以使用以下

进行测试
    public static void main(String... args) {
        List<BracketPair> results = new BracketFinder().findUnnecessaryBrackets("(( a + b ) + (( c + d ))) = a + b + c + d");
        for (BracketPair result : results) {
            System.out.println("Unneccessary brackets at indices " + result.start + "," + result.end);
        }
    }

答案 2 :(得分:1)

没有编程,但它可能看起来像这样:

给予操作+ / - 值1 给予操作*&amp; /值2 给出操作)(值2(和*一样)

1 go to inner parenthesis and check if the next operation is higher in its value (means the parenthesis is necessary) or equal/lower to the own operation. if equal or lower the parenthesis is not necessary.

2 go to 1

当两步之间没有变化

时,你已经完成了

希望这有帮助.. 如果你有解决方案,请告诉我。 如果这没有帮助,请告诉我:))

问候

答案 3 :(得分:1)

一次性可能。我们的想法是在每个()块周围查找上一个/下一个操作并应用关联性规则。当需要()时,这里是带有是/否标记的小表。

        // (a + b) + c NO
        // (a + b) - c NO
        // (a + b) / c YES
        // (a + b) * c YES

        // (a / b) + c NO
        // (a / b) - c NO
        // (a / b) / c NO
        // (a / b) * c NO

        // a + (b + c) NO
        // a - (b + c) YES
        // a / (b + c) YES
        // a * (b + c) YES

        // a + (b / c) NO
        // a - (b / c) NO
        // a / (b / c) YES
        // a * (b / c) NO


        // (a) ((a))   NO

这是C ++代码(我不确定它是否没有遗漏某些情况 - 它只是一个想法):

string clear(string expression)
{
    std::stack<int> openers;
    std::stack<int> closers;
    std::stack<bool> isJustClosed;
    std::stack<char> prevOperations;
    std::stack<bool> isComposite;
    std::stack<int> toDelete;


    prevOperations.push(' ');
    isJustClosed.push(false);
    isComposite.push(false);

    string result = expression + "@";
    for (int i = 0; i < result.length(); i++)
    {
        char ch = result[i];

        if ((ch == '*') || (ch == '/') || (ch == '+') || (ch == '-') || (ch == '(') || (ch == ')') || (ch == '@'))
            if (isJustClosed.size() > 0)
                if (isJustClosed.top() == true) {

                    // pop all and decide!
                    int opener = openers.top(); openers.pop();
                    int closer = closers.top(); closers.pop();
                    char prev = prevOperations.top(); prevOperations.pop();
                    char prevOperationBefore = prevOperations.top();
                    isJustClosed.pop(); //isJustClosed.push(false);
                    bool isComp = isComposite.top(); isComposite.pop();

                    bool ok = true;
                    if (prev == ' ')
                        ok = false;
                    else
                    {
                        ok = false;
                        if (((isComp) || (prev == '+') || (prev == '-')) && (ch == '/')) ok = true;
                        if (((isComp) || (prev == '+') || (prev == '-')) && (ch == '*')) ok = true;

                        if (((isComp) || (prev == '+') || (prev == '-')) && (prevOperationBefore == '-')) ok = true;
                        if (prevOperationBefore == '/') ok = true;
                        if (((isComp) || (prev == '+') || (prev == '-')) && (prevOperationBefore == '*')) ok = true;
                    }


                    if (!ok)
                    {
                        toDelete.push(opener);
                        toDelete.push(closer);
                    }
                }

        if (ch == '(') {
            openers.push(i);
            prevOperations.push(' ');
            isJustClosed.push(false);
            isComposite.push(false);
        }

        if (ch == ')') {
            closers.push(i);
            isJustClosed.top() = true;
        }

        if ((ch == '*') || (ch == '/') || (ch == '+') || (ch == '-')) {
            if (!isComposite.top())
            {
                char prev = prevOperations.top();
                if ((ch == '+') || (ch == '-'))
                    if ((prev == '*') || (prev == '/'))
                        isComposite.top() = true;
                if ((ch == '*') || (ch == '/'))
                    if ((prev == '+') || (prev == '-'))
                        isComposite.top() = true;
            }
            prevOperations.top() = ch;
            isJustClosed.top() = false;
        }


    }

    while (toDelete.size() > 0)
    {
        int pos = toDelete.top();
        toDelete.pop();
        result[pos] = ' ';
    }
    result.erase(result.size() - 1, 1);

    return result;
}

在每个块中,我们跟踪上一个操作,并跟踪内容是否像(a + b * c)那样复合。

测试:

void test()
    {
        LOG << clear("((a + (a + b))) - ((c)*(c) + d) * (b + d)") << NL;
        LOG << clear("a + (a + b) - ((c) + d) * (b + d)") << NL;
        LOG << clear("(a/b)*(c/d)") << NL;
        LOG << clear("(a/b)*((((c)/d)))") << NL;
        LOG << clear("((a + b) - (c - d))") << NL;
        LOG << clear("((a + b)*((c - d)))+c/d*((a*b))") << NL;
        LOG << clear("a+a*b*(a/b)") << NL;
        LOG << clear("a+a*b*(a+b)") << NL;
    }

结果:

  a +  a + b    - ( c * c  + d) *  b + d 
 a +  a + b  - ( c  + d) *  b + d 
a/b * c/d 
a/b *    c /d   
 a + b  - (c - d) 
(a + b)*  c - d   +c/d*  a*b  
a+a*b* a/b 
a+a*b*(a+b)

答案 4 :(得分:0)

我个人认为至少有两种方式:

<强>树

可以从输入表达式创建树。树创建后,它可以展平,没有无用的括号

波兰表示法

  • (( a + b ) + (( c + d )))将成为(+ (+ a b) (+ c d))
  • (( a + b ) * (( c + d )))将成为(* (+ a b) (+ c d))

从这里你可以比较每个操作数和因子,看它们在求解算术方程时是否具有相同的优先级

我会选择这棵树。