如何在程序中將中綴表達式轉換為後綴表達式?

比如

(31.8+3.2)/5

20*[(2.44-1.8)/0.4+0.15]

我試著把數字和運算符分開匹配,但是這樣無法知道閱讀順序。

這裡除數字外考慮的符號有+-*/()[]{}

如何轉換呢?


Shunting-yard algorithm 不客氣


這個問題對應的是調度場演算法。

讀懂下面這段話,也就學會如何轉換了:

規則:從左到右遍歷中綴表達式的每個數字和符號,若是數字就輸出,即成為後綴表達式的一部分;若是符號,則判斷其與棧頂符號的優先順序,是右括弧或優先順序低於棧頂符號(乘除優先加減)則棧頂元素依次出找並輸出,並將當前符號進棧,一直到最終輸出後綴表達式為止。

from 將中綴表達式轉化為後綴表達式 -- 簡明現代魔法

-

針對問題中示例的具體實現:

一。用字母代替數字,用小括弧代替中/大括弧 (為後續處理方便)

20*[(2.44-1.8)/0.4+0.15]

a=20, b=2.44, c=1.8, d=0.4, e=0.15,

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

二。實現調度場演算法

1. 在結尾加一結束符:a*((b-c)/d+e)#

2. 處理過程:

輸入 輸出 更新後的棧內容(自底到頂)
a a
* a *
( a *(
( a *((
b ab *((
lvl(-)=1, lvl(()=0
- ab *((-
c abc *((-
) abc- *(
lvl(/)=2, lvl(()=0
/ abc- *(/
d abc-d *(/
lvl(+)=1, lvl(/)=2
+ abc-d/ *(+
e abc-d/e *(+
) abc-d/e+ *
# abc-d/e+*

3. 將字母還原為數字:20 2.44 1.8 - 0.4 / 0.15 + *

-

幾個測試用例:

(31.8+3.2)/5 =&> 31.8 3.2 + 5 / =&> 7

20*[(2.44-1.8)/0.4+0.15] =&> 20 2.44 1.8 - 0.4 / 0.15 + * =&> 35

9+(3-1)*3+10/2 =&> 9 3 1 - 3 * + 10 2 / + =&> 20

5+((1+2)*4)-3 =&> 5 1 2 + 4 * + 3 - =&> 14

另外,後綴表達式是可以直接求值的,感興趣可以繼續實現下。

中綴表達式「5 + ((1 + 2) * 4) ? 3」寫作

5 1 2 + 4 * + 3 ?
下表給出了該逆波蘭表達式從左至右求值的過程,堆棧欄給出了中間值,用於跟蹤演算法。

輸入 操作 堆棧 注釋
5 入棧 5
1 入棧 5, 1
2 入棧 5, 1, 2
+ 加法運算 5, 3 (1, 2)出棧;將結果(3)入棧
4 入棧 5, 3, 4
* 乘法運算 5, 12 (3, 4)出棧;將結果(12)入棧
+ 加法運算 17 (5, 12)出棧;將結果 (17)入棧
3 入棧 17, 3
? 減法運算 14 (17, 3)出棧;將結果(14)入棧
計算完成時,棧內只有一個操作數,這就是表達式的結果:14

上述運算可以重寫為如下運算鏈方法(用於HP的逆波蘭計算器):[3]

1 2 + 4 * 5 + 3 ?

from 逆波蘭表示法

-

其他參考鏈接:

調度場演算法

Shunting-yard algorithm


我教你一個萬能的方法

如何手寫語法分析器

首先你先看完我的這篇博客,學會給你的表達式建立語法樹的類結構和遞歸向下的語法分析器。然後你不僅可以做中綴轉後綴,就算你想轉回來,去括弧,編譯成C++,什麼的都可以在這棵樹上面輕鬆遍歷完成。你順便學一下設計模式裡面的Visitor模式,所向披靡,提前掌握你後面可能要來知乎問的無窮多個問題。


後綴表達式比中綴表達式更適合使用棧來進行運算


你需要RPN,逆波蘭表達式的wiki上有詳細的描述。


中綴表達式轉換為後綴表達式的方法

a + b * c - (d + e)

  1. 按照運算符的優先順序對所有的運算單位加括弧。

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

  2. 轉換中綴與後綴表達式後綴:把運算符號移動到對應的括弧後面。

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

  3. 把括弧去掉,記得到了後綴表達式

    a b c * + d e + -

可以發現,後綴表達式是不需要括弧來調整運算優先順序的。

文/fever105(簡書作者)

原文鏈接:http://www.jianshu.com/p/ca1e5daf5c6b

著作權歸作者所有,轉載請聯繫作者獲得授權,並標註「簡書作者」。


實現過程其實也比較簡單。主要考慮使用Stack來存放信息就好。

以前去知道創宇面試的時候讓我寫過一次。

這是鏈接:KnownSec-Interview/rpn at master · shellvon/KnownSec-Interview · GitHub

代碼如下:

# encoding=utf8
# author:shellvon

import operator
import string

ARITHMETIC_OPERATORS = {
"+": operator.add, "-": operator.sub,
"*": operator.mul, "/": operator.div,
"%": operator.mod, "^": operator.pow,
"//": operator.floordiv,
}

class BadPostfixNotation(Exception):
"""NotImplemented"""
def __init__(self, message):
self._message = "BadPostfixNotation when build:{0}".format(message)

@property
def message(self):
return self._message

def validate_expr(expression):
"""
Validate the expression
"""
pass

def torpn(expr):
"""
A General Infix-to-Postfix Conversion.
The solution assumes that all expression are the right infix expression.
See:
http://interactivepython.org/runestone/static/pythonds/BasicDS/stacks.html
RPN:
Postfix Expressions(http://en.wikipedia.org/wiki/Reverse_Polish_notation)
For example:
A * B + C * D =&> A B * C D * +
( A + B ) * C - ( D - E ) * ( F + G ) =&>A B + C * D E - F G + * -
TODO:
check valid of the expr.
"""

postfixchar = string.letters+string.digits
precedence = {"^": 3, "*": 3, "/": 3, "+": 2, "-": 2, "(": 1}
token_list = expr.split()
postfix_stack, op_stack = [], []
for token in token_list:
if token in postfixchar:
postfix_stack.append(token)
elif token == "(":
op_stack.append(token)
elif token == ")":
tmp_token = op_stack.pop()
while tmp_token != "(":
postfix_stack.append(tmp_token)
tmp_token = op_stack.pop()
else:
while len(op_stack) != 0 and precedence[op_stack[-1]] &>= precedence[token]:
postfix_stack.append(op_stack.pop())
op_stack.append(token)
while len(op_stack) != 0:
postfix_stack.append(op_stack.pop())
return " ".join(postfix_stack)

def postfix(expr, operators=ARITHMETIC_OPERATORS):
"""
Postfix Evaluation
"""
stack = []

def func(stack, var):
stack[-2:] = [var] # lambda can"t use assginments.
[func(stack, operators[var](*stack[-2:])) if var in operators else stack.append(int(var)) for var in expr.split()]
return stack.pop()


已經有很多人給了思路了。我這裡再補充一個:遞歸下降翻譯成語法樹,然後後序遍歷就得到後綴式,前序遍歷就得到前綴式:

知乎專欄


用stack Infix, Prefix and Postfix Expressions 看完啥都懂了,起碼再有問題也知道怎麼搜索查找了


用棧寫,最近剛寫了個


https://github.com/generalthink/MyDataStructure/blob/master/src/main/java/com/generalthink/common/application/MyCalculator.java#L106

逆波蘭表達式的Java實現!


推薦閱讀:

如何高效地判斷兩個單鏈表是否有交叉?
希爾排序對於h有序數組排序的選擇問題?
演算法導論的學習路線是怎樣的?
windows下哪個C/C++開發工具最簡單最好用?
一個單鏈表,長度未知,如何快速的找出位於中間的那個元素?

TAG:演算法 | 正則表達式 | 數據結構 | 演算法與數據結構 |