从算术表达式中删除多余的括号

时间:2013-08-23 10:37:39

标签: algorithm data-structures stack permutation

这是一个面试问题,我没有在stackoverflow或外部找到任何令人满意的答案。问题陈述:

  

给定算术表达式,删除多余的括号。例如。   ((a * b)+ c)应成为a * b + c

我可以想到一种将中缀表达式转换为后置修复并将其转换回中缀的明显方法 - 但是有更好的方法吗?

7 个答案:

答案 0 :(得分:34)

当且仅当它们包含X%X%...%X形式的未表示的表达式时,必须使用一对括号,其中X是带括号的表达式或原子,%是二元运算符,并且如果至少有一个运算符%的优先级低于直接连接到其两侧的带括号的表达式的运算符;或者如果它是整个表达。所以例如在

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

周围的运算符是{+,*},括号内的最低优先级运算符是*,所以括号是不必要的。另一方面,在

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

括号内的优先级运算符+低于周围的运算符*,因此它们是必需的。但是,在

z * q + (a * b + c * d) + c

括号不是必需的,因为外部*未附加到带括号的表达式。

为什么这是真的如果表达式中的所有运算符(X%X%...%X)都具有比周围运算符更高的优先级,那么即使删除了括号,也无论如何都要先计算出内部运算符

因此,您可以通过此算法直接检查任何一对匹配的括号以获得冗余:

Let L be operator immediately left of the left parenthesis, or nil
Let R be operator immediately right of the right parenthesis, or nil
If L is nil and R is nil:
  Redundant
Else:
  Scan the unparenthesized operators between the parentheses
  Let X be the lowest priority operator
  If X has lower priority than L or R:
    Not redundant
  Else:
    Redundant

您可以对此进行迭代,删除冗余对,直到所有剩余对都是非冗余的。

示例:

((a * b) + c * (e + f))

(从左到右处理对):

((a * b) + c * (e + f))   L = nil R = nil --> Redundant
^                     ^   
 (a * b) + c * (e + f)    L = nil R = nil --> Redundant
 ^     ^                  L = nil R = + X = * --> Redundant
  a * b  + c * (e + f)    L = * R = nil X = + --> Not redundant
               ^     ^

最终结果:

a * b + c * (e + f)

答案 1 :(得分:2)

我刚刚找到答案:

前提是:

1. the expression has been tokenized
2. no syntax error
3. there are only binary operators

输入:

list of the tokens, for example:
   (, (, a, *, b, ), +, c, )

输出:

set of the redundant parentheses pairs (the orders of the pairs are not important),
for example,
   0, 8
   1, 5

请注意:该集合不是唯一的,例如,((a + b))* c,我们可以删除外部括号或内部括号,但最终表达式是唯一的

数据结构:

a stack, each item records information in each parenthese pair
the struct is:
   left_pa: records the position of the left parenthese
   min_op: records the operator in the parentheses with minimum priority
   left_op: records current operator

算法

1.push one empty item in the stack
2.scan the token list
    2.1 if the token is operand, ignore
    2.2 if the token is operator, records the operator in the left_op, 
        if min_op is nil, set the min_op = this operator, if the min_op 
        is not nil, compare the min_op with this operator, set min_op as 
        one of the two operators with less priority
    2.3 if the token is left parenthese, push one item in the stack, 
        with left_pa = position of the parenthese
    2.4 if the token is right parenthese, 
        2.4.1 we have the pair of the parentheses(left_pa and the 
             right parenthese)
        2.4.2 pop the item
        2.4.3 pre-read next token, if it is an operator, set it 
             as right operator
        2.4.4 compare min_op of the item with left_op and right operator
             (if any of them exists), we can easily get to know if the pair 
             of the parentheses is redundant, and output it(if the min_op
             < any of left_op and right operator, the parentheses are necessary,
             if min_op = left_op, the parentheses are necessary, otherwise
             redundant) 
        2.4.5 if there is no left_op and no right operator(which also means 
             min_op = nil) and the stack is not empty, set the min_op of top 
             item as the min_op of the popped-up item

实施例

示例一

((a*b)+c)
扫描到b后,我们有堆栈:

index left_pa min_op left_op
0
1     0       
2     1       *      *       <-stack top

现在我们遇到第一个')'(在第5位),我们弹出项目

left_pa = 1 
min_op = *
left_op = *

和预读算子'+',因为min_op priority'*'&gt; '+',所以对(1,5)是多余的,所以输出它。 然后扫描,直到我们遇到最后')',此刻,我们有堆栈

index left_pa min_op left_op
0
1     0       +      + 

我们弹出这个项目(因为我们遇到')'在pos 8),并预先读取下一个运算符,因为没有运算符,并且在索引0处,没有left_op,所以输出对(0,8)

示例二

a*(b+c)

当我们遇到')'时,堆栈就像:

index  left_pa  min_op left_op
0               *      *
1      2        +      +

现在,我们在index = 1处弹出项目,将min_op'+'与索引0处的left_op'*'进行比较,我们可以找出'(',')'是必要的

答案 2 :(得分:1)

  1. 在堆栈中推送一个空项目
  2. 扫描令牌列表

    2.1如果令牌是操作数,则忽略。

    2.2如果令牌是运算符,则将运算符记录在left_op中,  如果min_op为nil,则设置min_op = this运算符,如果是min_op  不是nil,将min_op与此运算符进行比较,将min_op设置为  两个优先级较低的运营商之一。

    2.3如果令牌是左括号,请在堆栈中按下一个项目,     with left_pa =括号的位置。

    2.4如果令牌是右括号:

    2.4.1我们有一对括号(left_pa和          右括号)

    2.4.2弹出项目

    2.4.3预读下一个标记,如果是操作符,则设置它          作为正确的运营商

    2.4.4将项目的min_op与left_op和right运算符进行比较          (如果它们中的任何一个存在),我们可以很容易地知道这对          括号中的多数是多余的,并输出它(如果是min_op          &LT;任何left_op和right运算符,括号都是必需的,          如果min_op = left_op,则括号是必要的,否则          多余的)

    2.4.5如果没有left_op且没有右操作符(这也意味着          min_op = nil)并且堆栈不为空,将min_op设置为top          item作为弹出项目的min_op 实例

答案 3 :(得分:0)

如果表达式有效,则此解决方案有效。我们需要将运算符映射到优先级值。

一个。从数组的两端遍历以找出两端的匹配括号。 让索引分别为i和j。

湾现在从i遍历到j并找出最低优先级运算符,它不包含在任何括号内。

℃。将此运算符的优先级与左括号左边的运算符和右括号右边的运算符进行比较。如果不存在此类运算符,则将其优先级视为-1。如果运算符的优先级高于这两个,请删除i和j处的括号。

d。继续步骤a到c,直到i <= j。

答案 4 :(得分:0)

以下代码是一个简单的解决方案,仅限于+-*/;如果您愿意,可以根据您的要求添加它们。

#include <iostream>
#include <stack>
#include <set>
using namespace std;

int size;
int loc;
set<char> support;
string parser(string input , int _loc){

    string expi;
    set<char> op;
    loc = _loc;

    while(1){
        if(input[loc] ==  '('){
            expi += parser(input,loc+1);
        }else if(input[loc] == ')'){
          if((input[loc+1] != '*') && (input[loc+1] != '/')){
              return expi;
          }else{
              if ((op.find('+') == op.end()) && (op.find('-') == op.end())){
                  return expi;
              }else{
                  return '('+expi+')';
              }
          }
        }else{
            char temp = input[loc];
            expi=expi+temp;
            if(support.find(temp) != support.end()){
                op.insert(temp);
            }
        }
        loc++;
        if(loc >= size){
            break;
        }
    }

    return expi;
}

int main(){
    support.insert('+');
    support.insert('-');
    support.insert('*');
    support.insert('/');

    string input("(((a)+((b*c)))+(d*(f*g)))");
    //cin >> input;
    size = input.size();

    cout<<parser(input,0);

    return 0;
}       

答案 5 :(得分:0)

以防万一有人在寻找快速简便的解决方案时发现此问题:如果您的表达式已被清理,并且您的语言(或库)为您的表达式提供了eval函数,您可以尝试删除一个括号将更改您的表达式的值。如果是这样,则必须保留括号。如果没有,则可以将其删除。

但是请记住,这当然不是有效的解决方案,而是“强力”路径。

这是Python中的示例实现,适用于整数和内置eval

def remove_brackets(term):
    a = 0
    while True:
        # Find opening bracket
        try:
            a = term.index("(", a)
        except ValueError:
            # No (more) opening brackets found
            break
        # Find corresponding closing bracket
        b = a
        while True:
            b = term.index(")", b + 1)
            if term[a + 1:b].count("(") == term[a + 1:b].count(")"):
                break
        # Assemble new term by removing current pair of brackets
        new_term = term[:a] + term[a + 1:b] + term[b + 1:]
        # If new term produces a different value, keep term as it is and try with the next pair of brackets
        if eval(term) != eval(new_term):
            a += 1
            continue
        # Adopt new term
        term = new_term
    return term

示例调用:

>>> remove_brackets("1 + (2 * 3)")
'1 + 2 * 3'
>>> remove_brackets("1 + (2 * 3) / 4")
'1 + 2 * 3 / 4'
>>> remove_brackets("1 + (2 * 3) / 4 / (5 * 6)")
'1 + 2 * 3 / 4 / (5 * 6)'

答案 6 :(得分:-4)

我认为您正在寻找一种算法,如下图所示。

这个算法“几乎”准备好了,因为一旦它变得越复杂就会出现很多错误,它就越复杂。我在这方面工作的方式是“即时编写和编写代码”,这意味着最多4个括号,事情很简单。但是在表达变得更加复杂之后,有些事情在写下纸上的想法时无法预测。然后编译器告诉我要纠正什么。如果我声明编写算法不是我,而是编写(C#)编译器,那就不是谎言!到目前为止,我花了1400行。并不是命令难以编写。他们的安排是一个真正的难题。您正在寻找的这个程序,具有非常高的复杂程度。好吧,如果您需要任何主要想法,请告诉我,我会回复。感谢名单!

Algorithm