Python 簡單計算器-逆波蘭后綴表達(dá)式實現(xiàn)

中綴表達(dá)式
9 + (3 - 1)*3 + 8 / 2
后綴表達(dá)式
9 3 1 - 3 * + 8 2 / +
簡易計算器晚凿,可以通過棧來實現(xiàn)。然而如果直接使用中綴表達(dá)式瘦馍,需要處理括號歼秽,而使用后綴表達(dá)式則可以規(guī)避這種麻煩。后綴表達(dá)式計算起來更加方便情组,步驟如下:

1.將后綴表達(dá)式入棧燥筷,數(shù)字直接入棧

2.遇到操作符,將棧頂?shù)膬蓚€元素出棧呻惕,
第一個出棧的是操作數(shù)荆责,第二個出棧的是被操作數(shù),
根據(jù)操作符計算兩個數(shù)的結(jié)果亚脆,然后將結(jié)果再次入棧

3.重復(fù)上面的步驟做院,直到后綴表達(dá)式遍歷結(jié)束,最后在棧中的元素便是計算結(jié)果

例:9 3 1 - 3 * + 8 2 / +
第一步:
  后綴表達(dá)式的第一個元素是9濒持,數(shù)字直接入棧键耕。
第二步:
  后綴表達(dá)式的第二個元素3,數(shù)字直接入棧柑营。
第三步:
  后綴表達(dá)式的第三個元素1屈雄,數(shù)字直接入棧。
第四步:
  后綴表達(dá)式的第四個元素‘-’官套,將棧頂?shù)膬蓚€元素出棧酒奶,
  第一個出棧的1作為操作數(shù),第二個出棧的3作為被操作數(shù)奶赔,
  而操作符是‘-’惋嚎,計算結(jié)果為:3 - 1 = 2,然后將2入棧站刑。
第五步:
  后綴表達(dá)式的第五個元素3另伍,數(shù)字直接入棧。
第六步:
  后綴表達(dá)式的第六個元素‘*’绞旅,將棧頂?shù)膬蓚€元素出棧摆尝,
  第一個出棧的3作為操作數(shù)温艇,第二個出棧的2作為被操作數(shù),
  而操作符是‘-’堕汞,計算結(jié)果為:2 * 3 = 6勺爱,然后將6入棧。
第七步:
  后綴表達(dá)式的第七個元素‘+’臼朗,將棧頂?shù)膬蓚€元素出棧邻寿,
  第一個出棧的6作為操作數(shù),第二個出棧的9作為被操作數(shù)视哑,
  而操作符是‘-’绣否,計算結(jié)果為:9 + 6 = 15挡毅,然后將15入棧。
第八步:
   后綴表達(dá)式的第八個元素8段磨,數(shù)字直接入棧耗绿。
第九步:
   后綴表達(dá)式的第九個元素2,數(shù)字直接入棧债蜜。
第十步:
  后綴表達(dá)式的第十個元素‘/’究反,將棧頂?shù)膬蓚€元素出棧,
  第一個出棧的2作為操作數(shù)狼速,第二個出棧的8作為被操作數(shù)卦停,
  而操作符是‘-’,計算結(jié)果為:8 / 2 = 4僵芹,然后將4入棧专执。
第十一步:
  后綴表達(dá)式的第十一個元素‘/’郁油,將棧頂?shù)膬蓚€元素出棧攀痊,
  第一個出棧的4作為操作數(shù)苟径,第二個出棧的15作為被操作數(shù)躬审,
  而操作符是‘-’,計算結(jié)果為:15 + 4 = 19遭殉,然后將19入棧博助。
第十二步:
  后綴表達(dá)式遍歷結(jié)束,棧中的元素就是最后的計算結(jié)果蛔糯。
在了解了后綴表達(dá)式的計算過程窖式,我們可以通過棧輕松實現(xiàn)該計算過程,那如何將中綴表達(dá)式轉(zhuǎn)化成后綴表達(dá)式淮逻?

中綴表達(dá)式轉(zhuǎn)化為后綴表達(dá)式

規(guī)則:從左到右遍歷中綴表達(dá)式的數(shù)字和符號蜒灰,若是數(shù)字就輸出,即成為后綴表達(dá)式的一部分凸椿;若是符號翅溺,則判斷其與棧頂符號的優(yōu)先級。是右括號或優(yōu)先級低于棧頂符號(乘除優(yōu)先加減)則棧頂元素依次出棧并輸出优幸,并將當(dāng)前符號進(jìn)棧网杆,一直到最終輸出后綴表達(dá)式為止。

具體實現(xiàn)步驟:中綴表達(dá)式轉(zhuǎn)化為后綴表達(dá)式可以通過棧和隊列來實現(xiàn)队秩,
需要一個棧昼浦,用來存操作符,需要一個隊列鸟蟹,用來存后綴表達(dá)式的結(jié)果使兔。

1.遍歷中綴表達(dá)式,如果是數(shù)字則直接入后綴表達(dá)式隊列

2.如果操作符棧為空锦针,操作符直接入操作符棧

3.如果操作符的優(yōu)先級大于操作符棧頂元素的優(yōu)先級置蜀,則直接入棧,
否則棧頂元素出棧馋吗,并添加到后綴表達(dá)式隊列中秋秤,直到棧頂元素小于操作符的優(yōu)先級,然后將該操作符入棧到操作符棧

4.如果操作符是右括號绍哎,操作符做出棧操作鞋真,且添加到后綴表達(dá)式隊列中,
直到左括號出棧海诲,左括號不需添加到后綴表達(dá)式隊列中

5.中綴表達(dá)式遍歷結(jié)束檩互,需將操作符棧的元素依次出棧,并添加到后綴表達(dá)式隊列中

例:9 + (3 - 1)*3 + 8 / 2
第一步:
  中綴表達(dá)式的第一個元素是9蚯斯,數(shù)字直接入后綴表達(dá)式隊列
  結(jié)果:
    中綴表達(dá)式隊列:9
    操作符棧:null
第二步:
  中綴表達(dá)式的第二個元素是‘+’,操作符棧為空拍嵌,符號‘+’直接入棧
  結(jié)果:
    中綴表達(dá)式隊列:9
    操作符棧:+
第三步:
  中綴表達(dá)式的第三個元素是‘(’,括號的優(yōu)先級大于操作符棧頂元素,‘(’直接入棧
  結(jié)果:
    中綴表達(dá)式隊列:9
    操作符棧:+ (
第四步:
  中綴表達(dá)式的第四個元素是3打洼,數(shù)字直接入后綴表達(dá)式隊列
  結(jié)果:
    中綴表達(dá)式隊列:9 3
    操作符棧:+ (
第五步:
  中綴表達(dá)式的第五個元素是‘-’,
  由于棧頂元素是‘(’操作符炫惩,‘(’操作符只有遇到‘)’操作符才會出棧阿浓,
  故‘-’直接入棧
  結(jié)果:
    中綴表達(dá)式隊列:9 3
    操作符棧:+ ( -
第六步:
  中綴表達(dá)式的第六個元素是1,數(shù)字直接入后綴表達(dá)式隊列
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1
    操作符棧:+ ( -
第七步:
  中綴表達(dá)式的第七個元素是‘)’筋蓖,遇到右括號退敦,棧頂元素依次出棧,
  并入到后綴表達(dá)式隊列中瓮下,直到‘(’出棧钝域,‘(’出棧后不做處理
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1 -
    操作符棧:+
第八步:
  中綴表達(dá)式的第八個元素是‘*’,優(yōu)先級高于操作符棧棧頂元素‘+’的優(yōu)先級路呜,故直接入棧
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1 -
    操作符棧:+ *
第九步:
  中綴表達(dá)式的第九個元素是3织咧,數(shù)字直接入后綴表達(dá)式隊列
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1 - 3
    操作符棧:+ *
第十步:
  中綴表達(dá)式的第十個元素是‘+’,優(yōu)先級不大于操作符棧棧頂元素巡社,
  棧頂元素出棧手趣,并入到后綴表達(dá)式隊列中肥荔,
  直到棧頂元素的優(yōu)先級小于該元素的優(yōu)先級燕耿,最后該元素入棧到操作符棧
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1 - 3 * +
    操作符棧:+ 
第十一步:
  中綴表達(dá)式的第十一個元素是8姜胖,數(shù)字直接入后綴表達(dá)式隊列
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1 - 3 * + 8
    操作符棧:+ 
第十二步:
  中綴表達(dá)式的第十二個元素是‘/’,優(yōu)先級高于操作符棧棧頂元素的優(yōu)先級蚜锨,故直接入棧
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1 - 3 * + 8
    操作符棧:+ /
第十三步:
  中綴表達(dá)式的第十三個元素是2慢蜓,數(shù)字直接入后綴表達(dá)式隊列
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1 - 3 * + 8 2
    操作符棧:+ /
第十四步:
  中綴表達(dá)式遍歷結(jié)束,將操作符棧棧頂元素依次出棧氛悬,并入到后綴表達(dá)式的隊列中
  結(jié)果:
    中綴表達(dá)式隊列:9 3 1 - 3 * + 8 2 / +
    操作符棧:null

代碼

class Calculator(object):

    def __init__(self):
        # 操作符集合
        self.operators = ['+', '-', '*', '/', '(', ')']
        # 操作符優(yōu)先級
        self.priority = {
            '+': 1,
            '-': 1,
            '*': 2,
            '/': 2,
            '(': 3,
            ')': 3
        }

    def generate_postfix_expression(self, expression):
        """
        生成后綴表達(dá)式
        :param expression:
        :return:
        """

        # 去除表達(dá)式中所有空格
        expression = expression.replace(' ', '')

        # 創(chuàng)建list作為棧如捅,list的append和pop方法剛好和棧的入棧和出棧方法相同
        # 操作符棧
        operator_stack = list()
        # 后綴表達(dá)式是從尾部插入數(shù)據(jù)调煎,使用后綴表達(dá)式計算結(jié)果是從頭開始遍歷,可以理解為先進(jìn)先出烈涮,可以使用隊列實現(xiàn)窖剑,
        # 這里為了簡便用list代替,不做內(nèi)存釋放處理
        expression_stack = list()

        for element in expression:
            # 如果是數(shù)字則直接入表達(dá)式棧
            if element in self.operators:

                # 如果棧為空讶舰,操作符直接入操作符棧需了,或者為左括號,也直接入操作符棧
                if not operator_stack:
                    operator_stack.append(element)
                else:
                    # 如果目標(biāo)元素是右括號鹅颊,操作符棧頂出棧直接遇到左括號墓造,且出棧的操作符除了括號入到表達(dá)式隊列中
                    if element == ')':
                        for top in operator_stack[::-1]:
                            if top != '(':
                                expression_stack.append(top)
                                operator_stack.pop()
                            else:
                                operator_stack.pop()
                                break
                    else:
                        for top in operator_stack[::-1]:
                            # 如果目標(biāo)元素大于棧頂元素锚烦,則直接入棧涮俄,否則棧頂元素出棧尸闸,入到表達(dá)式隊列中
                            # 左括號只有遇到右括號才出棧
                            if self.priority[top] >= self.priority[element] and top != '(':
                                expression_stack.append(top)
                                operator_stack.pop()
                            else:
                                operator_stack.append(element)
                                break

                        # 可能操作符棧所有的元素優(yōu)先級都大于等于目標(biāo)操作符的優(yōu)先級,這樣的話操作符全部出棧了苞尝,
                        # 而目標(biāo)操作符需要入棧操作
                        if not operator_stack:
                            operator_stack.append(element)

            else:
                expression_stack.append(element)

        # 中綴表達(dá)式遍歷結(jié)束茧痕,操作符棧仍有操作符踪旷,將操作符棧中的操作符入到表達(dá)式棧中
        for i in range(len(operator_stack)):
            expression_stack.append(operator_stack.pop())

        return expression_stack

    def calcaulate(self, expression):
        # 生成后綴表達(dá)式
        expression_result = self.generate_postfix_expression(expression)
        # 使用list作為棧來計算
        calcalate_stack = list()

        # 遍歷后綴表達(dá)式
        for element in expression_result:
            # 如果為數(shù)字直接入棧
            # 遇到操作符令野,將棧頂?shù)膬蓚€元素出棧
            if element not in self.operators:
                calcalate_stack.append(element)
            else:
                # 操作數(shù)
                number1 = calcalate_stack.pop()
                # 被操作數(shù)
                number2 = calcalate_stack.pop()
                # 結(jié)果 =  被操作數(shù) 操作符 操作數(shù) (例:2 - 1)
                result = self.operate(number1, number2, element)
                # 計算結(jié)果入棧
                calcalate_stack.append(result)

        return calcalate_stack[0]

    def operate(self, number1, number2, operator):
        """
        計算結(jié)果
        :param number1: 操作數(shù)
        :param number2: 被操作數(shù)
        :param operator: 操作符
        :return: 
        """
        number1 = int(number1)
        number2 = int(number2)

        if operator == '+':
            return number2 + number1
        if operator == '-':
            return number2 - number1
        if operator == '*':
            return number2 * number1
        if operator == '/':
            return number2 / number1


if __name__ == '__main__':
    c = Calculator()
    expression = '9 + (3 - 1)*3 + 8 / 2'
    expression_result = c.calcaulate(expression)
    print(expression_result)

代碼還有許多小問題气破,還需要優(yōu)化餐抢,比如時間復(fù)雜度還要降低,中綴表達(dá)式?jīng)]法正確拆分旷痕,數(shù)字是十位以上的就會出錯了,十位個位等會被拆成單個字符售碳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贸人,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子艺智,更是在濱河造成了極大的恐慌圾亏,老刑警劉巖碗誉,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哮缺,死亡現(xiàn)場離奇詭異甲喝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)糠溜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門非竿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人红柱,你說我怎么就攤上這事蓖乘。” “怎么了零聚?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵些侍,是天一觀的道長。 經(jīng)常有香客問我沿腰,道長狈定,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任措嵌,我火速辦了婚禮芦缰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浪规。我一直安慰自己或听,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布誉裆。 她就那樣靜靜地躺著足丢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斩跌。 梳的紋絲不亂的頭發(fā)上捞慌,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機(jī)與錄音袖订,去河邊找鬼锻霎。 笑死揪漩,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奄容。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼蜀细,長吁一口氣:“原來是場噩夢啊……” “哼奠衔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起归斤,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤脏里,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后迫横,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡恨狈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年介返,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刃宵。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡徘公,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出坦袍,到底是詐尸還是另有隱情,我是刑警寧澤捂齐,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布奠宜,位于F島的核電站,受9級特大地震影響压真,放射性物質(zhì)發(fā)生泄漏蘑险。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一泼差、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堆缘,春花似錦柴信、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唆鸡。三九已至,卻和暖如春燃逻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伯襟。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工握童, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稽揭。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓肥卡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親步鉴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容