数据结构入门(二)栈的应用之数学表达式求值

  在文章数据结构入门(一)栈的实现中,我们已经知道了如何去实现“栈”这个数据结构,并且介绍了一个它的应用:数的进制转换。在本文中,将会介绍栈的第二个应用,也就是栈在数学表达式求值中的应用。
  我们分以下几步对数学表达式进行求值。

  • 栈的实现;
  • 中缀表达式转后缀表达式;
  • 后缀表达式求值。

先不着急明白上述术语,你看下去就会明白了。

栈的实现

  以下是栈的Python实现(Stack.py),代码如下:

# -*- coding: utf-8 -*-

class Empty(Exception):
    # Error attempting to access an element from an empty container
    pass

class Stack:
    # initialize
    def __init__(self):
        self.__data = []

    # length of Stack
    def __len__(self):
        return len(self.__data)

    # whether the Stack is empty
    def is_empty(self):
        return len(self.__data) == 0

    # push an element is Stack
    def push(self, e):
        self.__data.append(e)

    # top element of Stack
    def top(self):
        if self.is_empty():
            raise Empty('Stack is empty')
        return self.__data[-1]

    # remove the top element of Stack
    def pop(self):
        if self.is_empty():
            raise Empty('Stack is empty')
        return self.__data.pop()

    # clear the Stack
    def clear(self):
        while not self.is_empty():
            self.pop()

中缀表达式转后缀表达式

  首先,我们来看一下数学表达式的三种形式:前缀表达式,中缀表达式,后缀表达式。
  中缀表达式(Infix Expression)就是我们平时常用的书写方式,带有括号。前缀表达式(Prefix Expression)要求运算符出现在运算数字的前面,后缀表达式(Postfix Expression)要求运算符出现在运算数字的后面,一般这两种表达式不出现括号。示例如下:

中缀表达式 前缀表达式 后缀表达式
A + B * C + D + + A * B C D A B C * + D +
(A + B) * (C + D) * + A B + C D A B + C D + *
A * B + C * D + * A B * C D A B * C D * +
A + B + C + D + + + A B C D A B + C + D +

一般在计算机中,为了方便对表达式求值,我们需要将熟悉的中缀表达式转化为后缀表达式。
  中缀表达式转后缀表达式的算法如下:

  1. 创建一个空栈opstack,用于储存运算符。创建一个空的列表,用于储存输出结果。
  2. 将输入的中缀表达式(字符串形式)用字符串的split方法转化为一个列表。
  3. 从左到右对该列表进行遍历操作(元素为token),如下:
    • 如果token为运算数,则将它添加(append)至输出列表中。
    • 如果token为左小括号,则将它压入(psuh)到opstack中。
    • 如果token是右小括号,则对opstack进行pop操作,直至对应的左小括号被移出。将移出的运算符添加(append)到输出列表的末端。
    • 如果token是 *, /, +, -, 中的一个,则将其压入(push)到opstack中。注意,先要移除那些运算优先级大于等于该token的运算符,并将它们添加到输出列表中。
  4. 当上述过程结果后,检查opstack。任何还在opstack中的运算符都应移除,并将移出的运算符添加(append)到输出列表的末端。

  上述过程的完整Python代码如下:

# -*- coding: utf-8 -*-
from Stack import Stack

# 中缀表达式转化为后缀表达式
def infixToPostfix(infixexpr):
    prec = {"*": 3, "/": 3, "+": 2, "-": 2, "(": 1}
    opStack = Stack()
    postfixList = []
    tokenList = infixexpr.split()

    for token in tokenList:
        if token.isdigit() or '.' in token:
            postfixList.append(token)
        elif token == '(':
            opStack.push(token)
        elif token == ')':
            topToken = opStack.pop()
            while topToken != '(':
                postfixList.append(topToken)
                topToken = opStack.pop()
        else:
            while (not opStack.is_empty()) and (prec[opStack.top()] >= prec[token]):
                  postfixList.append(opStack.pop())
            opStack.push(token)

    while not opStack.is_empty():
        postfixList.append(opStack.pop())
    return " ".join(postfixList)

# inExpr = "( ( 1 + 2 ) * 3 ) * ( 3 - 1.2 )"
inExpr = "10 + 3 * 5 / ( 16 - 4 )"
postExpr = infixToPostfix(inExpr)
print(postExpr)

输出结果如下:

10 3 5 * 16 4 - / +

后缀表达式求值

  当把中缀表达式转化为后缀表达式之后,我们再利用栈对后缀表达式求值。其具体的算法如下:

  1. 建立一个栈来存储待计算的运算数;
  2. 遍历字符串,遇到运算数则压入栈中,遇到运算符则出栈运算数(2次),进行相应的计算,计算结果是新的操作数,压入栈中,等待计算;
  3. 按上述过程,遍历完整个表达式,栈中只剩下最终结果;

  完整的Python代码如下:(接以上代码)

# -*- coding: utf-8 -*-
from Stack import Stack

# 两个数的运算, 除法时分母不为0
def operate(op, num1, num2):
    if num2 == 0:
        raise ZeroDivisionError
    else:
        res = {
                '+': num1 + num2,
                '-': num1 - num2,
                '*': num1 * num2,
                '/': num1 / num2,
              }
        return res[op]

# 将字符串转化为浮点型或整型数字
def str2num(s):
    if '.' in s:
        return float(s)
    else:
        return int(s)

# 后缀表达式求值
def evalPostfix(e):

    tokens = e.split()  # 后缀表达式转化为列表
    s = Stack()
    for token in tokens:
        if token.isdigit() or '.' in token:  # 如果当前元素是数字
            s.push(str2num(token))
        elif token in '+-*/':  # 如果当前元素是运算符
            op2 = s.pop()
            op1 = s.pop()
            s.push(operate(token, op1, op2))  # 计算结果入栈
    return s.pop()

# inExpr = "( ( 1 + 2 ) * 3 ) * ( 3 - 1.2 )"
inExpr = "10 + 3 * 5 / ( 16 - 4 )"
postExpr = infixToPostfix(inExpr)
print(postExpr)
result = evalPostfix(postExpr)
print(result)

输出结果:

11.25

请务必注意,我们输入的中缀表达式中,每个运算符或运算符要用空格隔开。

参考文献

  1. 3.9. Infix, Prefix and Postfix Expressions: http://interactivepython.org/runestone/static/pythonds/BasicDS/InfixPrefixandPostfixExpressions.html
  2. Python算法实战系列之栈: http://python.jobbole.com/87581/

注意:本人现已开通微信公众号: Python爬虫与算法(微信号为:easy_web_scrape), 欢迎大家关注哦~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容