码迷,mamicode.com
首页 > 其他好文 > 详细

栈实现以及逆波兰表达式

时间:2018-02-07 00:38:09      阅读:214      评论:0      收藏:0      [点我收藏+]

标签:对应关系   元素   链表   后缀表达式   位置   数据结构   五步   定义   去掉   

栈和队列也是数据结构中经常用到的一种容器.栈是先进后出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

栈实现以及逆波兰表达式

标签:对应关系   元素   链表   后缀表达式   位置   数据结构   五步   定义   去掉   

原文地址:https://www.cnblogs.com/zhanghongfeng/p/8424543.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!