web-dev-qa-db-ja.com

算術式から冗長な括弧を削除します

これはインタビューの質問ですが、stackoverflowや外部で十分な答えは見つかりませんでした。問題文:

算術式を指定して、余分な括弧を削除してください。例えば。 ((a * b)+ c)はa * b + cになるはずです

Infix式をpost fixに変換し、それをinfixに戻す明白な方法を考えることができますが、これを行うより良い方法はありますか?

16
Darth.Vader

括弧のペアが必要なのは、X%X%...%Xの形式の括弧なしの式を囲む場合だけです。ここで、Xは括弧付きの式またはアトムであり、%は2項演算子であり、少なくとも1つの演算子の場合%は、括弧で囲まれた式のいずれかの側に直接付加された演算子よりも優先順位が低くなります。またはそれが表現全体である場合。したがって、たとえばに

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)
39
Antti Huima

私は答えを見つけました:

前提条件は次のとおりです。

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

例1

((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優先度「*」>「+」なので、pair(1,5)は冗長であるため、出力します。次に、最後の ')'が見つかるまでスキャンします。現時点では、スタックがあります。

index left_pa min_op left_op
0
1     0       +      + 

このアイテムをポップします(pos 8)で ')'を満たし、次の演算子を先読みします。演算子がなく、インデックス0に、left_opがないため、pair(0、8)を出力します。

例2

a*(b+c)

「)」を満たすと、スタックは次のようになります。

index  left_pa  min_op left_op
0               *      *
1      2        +      +

ここで、インデックス= 1でアイテムをポップし、min_op '+'とインデックス0のleft_op '*'を比較します。 '('、 ')'が必要であることを確認できます

3
lucian

このソリューションは、式が有効な場合に機能します。演算子を優先度の値にマッピングする必要があります。

a。配列の両端からトラバースして、両端の括弧を突き合わせます。インデックスをそれぞれiとjとします。

b。次に、iからjに移動して、括弧内に含まれていない優先順位の最も低い演算子を見つけます。

c。この演算子の優先順位を、左括弧の左側および右括弧の右側の演算子と比較します。そのような演算子が存在しない場合は、その優先順位を-1として扱います。演算子の優先順位がこれら2つより高い場合は、iとjの括弧を削除します。

d。手順a〜cをi <= jまで続けます。

1
Raj
  1. スタック内の1つの空のアイテムをプッシュする
  2. トークンリストをスキャンする

    2.1トークンがオペランドの場合は無視してください。

    2.2トークンが演算子の場合、演算子をleft_opに記録し、min_opがnilの場合、min_op = this演算子を設定します。min_opがnilでない場合、min_opをこの演算子と比較し、min_opを2つの演算子の1つとして設定します。優先。

    2.3トークンが左括弧である場合は、left_pa =括弧の位置で、スタックに1つのアイテムをプッシュします。

    2.4トークンが右括弧である場合:

    2.4.1括弧のペア(左括弧と右括弧)があります。

    2.4.2アイテムをポップする

    2.4.3次のトークンを先読みし、それが演算子である場合、それを正しい演算子として設定する

    2.4.4アイテムのmin_opをleft_opおよびright演算子と比較し(存在する場合)、括弧のペアが冗長であるかどうかを簡単に確認し、それを出力できます(min_op <left_opおよびright演算子のいずれかである場合、括弧が必要です。min_op= left_opの場合は括弧が必要です。それ以外の場合は冗長です)

    2.4.5 left_opとright演算子(min_op = nilも意味する)がなく、スタックが空でない場合、ポップアップアイテムの例のmin_opとしてトップアイテムのmin_opを設定する

1
xyz1

誰かがすばやく簡単な解決策を探しているときにこの質問を見つけた場合に備えて:式がサニタイズされており、言語(またはライブラリ)が式に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)'
0
finefoot

以下のコードは、+-*/に限定された簡単なソリューションです。必要に応じて、要件に応じて追加できます。

#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;
}       
0
nagu