zoukankan      html  css  js  c++  java
  • 栈实现以及逆波兰表达式

    栈和队列也是数据结构中经常用到的一种容器.栈是先进后出FILO,队列是先进先出FIFO.在C语言中,栈可以用数组或者链表来实现,在python中,list也就是列表也可以当做栈使用.比如在尾部压入元素可以用append的方法,压出元素可以用pop的方法.访问栈定元素可以用list[-1]的方法.但是list也同时提供了一大批栈不应该支持的操作,比如栈中未弹出的元素是存在的,但是list可以删除任意元素.因此为了实现完全的栈功能,我们可以用一个类来实现栈并将list隐藏在这个对象的内部.代码如下

    在内部定义了一个私有变量_elem.使得无法从外部实例进行访问,也就无从修改

    class stack():

        def __init__(self):

            self._elem=[]

        def is_empty(self):

            return self._elem == []

        def top(self):

            if self._elem == []:

                raise BaseException('top:stack empty')#当列表为空的时候,抛出异常

            return self._elem[-1]

        def push(self,elem):

            self._elem.append(elem)

        def pop(self):

            if self._elem == []:

                raise BaseException('pop:stack empyt')

            return self._elem.pop()

    if __name__=="__main__":

        s=stack()

        s.push(5)

        s.push(4)

        s.push(10)

    print s.pop()

    前面这个例子是通过内置的list来实现了栈队列,我们还可以采用列表的形式来实现栈

    代码如下:

    这里首先引用了第三章中的Lnode对象

    class Lnode(object):

        def __init__(self,elem,next_=None):

            self.elem=elem

            self.next=next_

    class Stack_node():

        def __init__(self):

            self._top=None

        def is_emepy(self):

            return self._top == None

        def top(self):

            if self._top== None:

                raise BaseException("top:stack is empty")

            return self._top.elem

        def push(self,elem):

            self._top=Lnode(elem,self._top) #通过传入上一个Lnode对象进行链表链接

        def pop(self):

            if self._top == None:

                raise BaseException('pop:stack is empty')

            p=self._top

            self._top=p.next

            return p.elem

    if __name__=="__main__":

        s=Stack_node()

        s.push(4)

        s.push(10)

    print s.pop()

    前面2个列子介绍了栈的两种实现方式,下面来看下栈的具体应用.首先来看第一个例子.括号匹配.在数字运算中有(),[],{}三种符号来进行各种封闭的运算.对于一个正常的计算公式而言,当前比括号应该与前面最近的尚未匹配的开括号匹配,下一个闭括号应与前面次进的括号匹配.因此在这里我们就可以应用到栈来实现括号的匹配.来看下具体的实现

    def check_parents(text):

    #首先定义parents代表所有的括号形式,然后定义open_parents表示所有的开括号

    然后定义oppsites一个字典,代表开括号和闭括号的对应关系.

        parents='()[]{}'

        open_parents='([{'

        oppsites={")":"(","]":"[","}":"{"}

        def parent_generate(text):

            i,text_len=0,len(text)

            while True:

                while i< text_len and text[i] not in parents: #当不属于括号的,则继续往后

                    i+=1

                if i >= text_len:

                    return

                yield text[i],i  #属于括号形式的则通过yield返回

                i+=1

        s=stack()

        for pt,i in parent_generate(text): #调用parent_generate生成器

            if pt in open_parents: #如果是开括号则进栈

                s.push(pt)

            elif s.pop()!=oppsites[pt]: #否则是闭括号,则与最近的一次进栈的括号进行比较,看是否匹配,如果不匹配则提示umatch

                print 'unmatch was found at %d' % i

                return False

            else: #如果匹配则不做任何操作

                pass

        print 'all are matched'

        return True

    if __name__=="__main__":

    check_parents('{[(a+b)+c]/3+1}+2')

    运行结果:

    /usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

    all are matched

    如果修改下传入的textcheck_parents('{[(a+b)+c/3+1}+2'),少了一个].则会提示umatch,并指示不匹配的位置.

    /usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

    unmatch was found at 13

    后缀表达式

    算是表达式有三种表达方式 中缀表达式,前缀表达式,后缀表达式.来看下三种表达式的区别

    中缀表达式: (3-5)*2+4

    前缀表达式:+*-3524

    后缀表达式:35-2*4+

    可以看到中缀表达式就和我们平常书写的运算公式是一样的,中缀表达式就是将运算符号写在运算符的前面,而后缀表达式就是将运算符号写在运算符的后面

    其实表达式之间的转换很简单,可以参考如下的方法:

    给出一个中缀表达式:a+b*c-(d+e) 

    第一步:按照运算符的优先级对所有的运算单位加括号:式子变成拉:((a+(b*c))-(d+e)) 

    第二步:转换前缀与后缀表达式 

    前缀:把运算符号移动到对应的括号前面 

    则变成拉:-( +(a *(bc)) +(de)) 

    把括号去掉:-+a*bc+de 前缀式子出现 

    后缀:把运算符号移动到对应的括号后面 

    则变成拉:((a(bc)* )+ (de)+ )- 

    把括号去掉:abc*+de+- 后缀式子出现 

    那么这几种表达式有什么意义呢,中缀表达式对于我们正常手写运算来说很形象,但是对于计算机来说就很抽象了,而前缀表达式和后缀表达式来说则很形象.比如3+(2-5)*6/3这个表达式,对应的后缀表达式为3 2 5 - 6 3 / * +

    那么在计算借助到栈的方法进行运算

    1 如果是数字则直接进栈此时在栈的数据为523

    2 遇到运算符则将栈顶的2个数字出栈进行计算,此时遇到-则5,2出栈,进行运算2-5=-3并将-3重新入栈.并继续遍历表达式,此时栈的数据为36-33

    3 遇到/则继续出栈2个数字进行运算6/3=2并将2入栈,此时栈的数据为2-33

    4 遇到*则继续出栈2个数字进行运算2*-3=-6并入栈,此时栈数据为-63

    5 遇到+则出栈2个数字进行运算-6+3=-3并入栈,此时栈数据为-3

    这样就得到了最终的数据.从上面的表示可以看出后缀表达式对于计算机的运算来说很方便,下面就来看下后缀表达式的实现:以:3+2*5-6/3为例.

    有个数据结构一个是后缀表达式队列

    一个是运算符栈队列

    a 遇到数字则加入后缀表达式列表

    b遇到运算符或者是(则进运算符栈

    c当遇到准备进栈的运算符优先级小于或等于栈顶的运算符,则将栈顶运算符出栈加入到后缀表达式列表中

    d如果准备进栈的运算符优先级大于栈顶的运算符则继续进栈

    第一步:数字3进入后缀表达式,同时+进入运算符栈

    后缀表达式队列      运算符栈

    3            +

    第二步:数字2进入后缀表达式,同时*进入运算符栈

    后缀表达式队列      运算符栈

    32           *+

    第三步:数字5进入后缀表达式,同时-准备进入运算符栈,由于-的优先级小于当前栈顶的*,因此*出栈并加入到后缀表达式队列中,并且-和+的优先级相同,因此+继续出栈并加入到后缀表达式队列中.最后-进栈

    后缀表达式队列      运算符栈

    325*+        -

    第四步:数字6进入后缀表达式,/进入运算符栈

    后缀表达式队列      运算符栈

    325*+6       /-

    第五步:数字3进入后缀表达式.此时将运算符依次出栈链接到后缀表达式队列之后.

    后缀表达式队列      运算符栈

    325*+63      /-

    最终生成的后缀表达式为325*+63/-

    下面来看下具体的代码实现,首先是后缀表达式生成的代码:

     

    def trans_infix_suffix(line):

        s=stack()  #运算符栈

        exp=[]   #后缀表达式队列

        priority={"(":1,"+":3,"-":3,"*":5,"/":5}  #生成运算符优先级大小的字典

        infix_operator="+-/*()"

        for x in line:

            if x not in infix_operator: #如果不是运算符号,则进后缀表达式队列

                exp.append(x)

            elif s.is_empty() or x == '(': #如果是(则直接进栈

                s.push(x)

            elif x == ')': #如果是),则运算符出栈并加入到后缀表达式中,直到遇见'('

                while not s.is_empty() and s.top() != '(':

                    exp.append(s.top)

                if s.is_empty():

                    raise BaseException('stack is empty')

                s.pop()  # '(' 出栈

            else: #比较栈顶的元素和当前运算符的优先级,如果大于则出栈加入到后缀表达式队列中.知道栈顶元算符优先级小于当前运算符的优先级

                while not s.is_empty() and priority[s.top()] >= priority[x]:

                    exp.append(s.pop())

                s.push(x) #当前运算符入栈

        while not s.is_empty(): #栈运算符的运算符全部出栈加入到后缀表达式队列中.

            if s.top() == '(':

                raise BaseException('extra ( found')

            exp.append(s.pop())

    return exp

    得到了后缀表达式,那么下一步就是要针对后缀表达式进行运算了,代码如下:

    def exp_calculate(exp):

        operators='+-/*'

        s=stack()

        for x in exp:

            if x not in operators: #如果是非运算符,则直接进栈

                s.push(int(x))

                continue

            if s.depth() < 2:

                raise BaseException('lack of stack element')

            a=s.pop() #否则遇到运算符,则出栈2个数字进行运算

            b=s.pop()

            if x == '+':

                c=b+a

            elif x == '-':

                c=b-a

            elif x == '*':

                c=b*a

            elif x == '/':

                c=b/a

            else:

                break

            s.push(c)  #将元算得到的数字进栈

     

        if s.depth() == 1:  # 运算完返回最终的结果

            return s.pop()

    对于3+2*5-6/3这个表达式,得到运算结果为11.运算正确

    /usr/bin/python2.7 /home/zhf/py_prj/data_struct/chapter5.py

    ['3', '2', '5', '*', '+', '6', '3', '/', '-']

    11

  • 相关阅读:
    js 中 undefined 和null 的区别
    【Gym103107E/黑龙江省赛16thE】Elastic Search(Trie树性质+ac自动机性质)
    不等概率抽卡的毕业期望次数
    博客园无法用IE进行登录
    Web项目开发小结
    各位看官,自己觉着喜欢的存到手机里面
    MVC控制器执行重定向
    吐了个槽o.o
    浏览器设置不缓存的方法
    关于A+B
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/8424543.html
Copyright © 2011-2022 走看看