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

第7章:栈

时间:2018-11-01 22:22:04      阅读:202      评论:0      收藏:0      [点我收藏+]

标签:ddl   initial   ext   family   nta   index   bee   plist   替代   

      • 栈概览
        • 栈是线性集合,遵从后进先出原则( Last - in first - out LIFO )原则
        • 栈常用的操作包括压入( push ) 和弹出( pop )
          • 技术分享图片
        • 栈的应用
          • 将中缀表达式转换为后缀表达式,并且计算后缀表达式的值
          • 回溯算法
          • 管理计算机内存以支持函数和方法调用
          • 支持应用程序中的撤消功能
          • 维护Web浏览器访问链接的历史记录
      • 使用栈
        • 栈不是Python的内建类型,可以用列表代替,但是列表可以在任何位置插入,删除,替换元素,这些违背了栈作为一种抽象数据类型的本意,因此需要为栈定义一种更为严格的接口
        • 栈接口
          • 技术分享图片
        • 初始化一个栈
          • s1 = ArrayStack()

            s2 = LinkedStack( [20, 40, 60] ) 

        • 示例应用程序:匹配括号
          • 步骤
            • 扫描表达式,将开始的括号压入栈中
            • 当遇到一个结束的括号时,如果栈为空,或者如果栈项的项不是相同的类型开始括号,括号不匹配
            • 如果是正确类型的括号,从栈顶弹出一项,继续扫描表达式
            • 当到达表达式未尾时,栈应为空,否则括号不匹配
          • 代码
            • index 方法返回列表中项的位置
            • 可以自定义括号的类型
            • #!/usr/bin/env python

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

              # Author:Lijunjie

               

              """

              File: brackets.py

              Checks expressions for matching brackets.

              """

               

              from linkedstack import LinkedStack

               

               

              def bracketsBalance( exp, startBracketList = [‘(‘, ‘[‘], endBracketList = [‘)‘, ‘]‘] ):

                  """exp is a string that represents the expressions.

                     startBracketList is the list of start brackets list.

                     endBracketList is the list of end brackets list.

                     precondition: startBracketList must have the same length of endBracketList.

                     raise: Exception if startBracketList don‘t have the same length of endBracketList."""

                 

                  if len( startBracketList ) != len( endBracketList ):

                      raise Exception( "The startBracketList must have the same length with the endBracketList." )

                 

                  stk = LinkedStack()

                  for ch in exp:

                      if ch in startBracketList:

                          stk.push( ch )

                      elif ch in endBracketList:

                          if stk.isEmpty():

                              return False

                          chFromStack = stk.pop()

                          if chFromStack != startBracketList[ endBracketList.index( ch ) ]:

                              return False

                  return stk.isEmpty()

                 

              def main():

                  """Test the bracketsBalance function."""

                  while True:

                      exp = input( "Enter a brackets expressions: ")

                      if exp == "":

                          break

                      if bracketsBalance(exp,[‘(‘,‘[‘, ‘{‘],[‘)‘, ‘]‘, ‘}‘] ):

                          print("OK!")

                      else:

                          print( "Not OK!" )

               

               

              if __name__ == "__main__":

                  main()

 

      • 栈的 3 种应用
        • 计算算术表达式
          • 中缀表达式
            • 运算符位于两个运算数之间
            • 计算涉及优先级问题,有时需要括号来表示优先级
          • 后缀表达式
            • 运算符紧跟在运算数之后
            • 没有优先级问题,一遇到运算符就立即运用
          • 示例
            • 技术分享图片
        • 计算后缀表达式
          • 步骤(需要一个数字栈
            • 从左到右的遍历表达式,遇到运算数,则将其压入数字栈中
            • 碰到一个运算符时,从数字栈中弹出两个运算数,对其应用运算符,并将所得的结果压入数字栈
            • 继续遍历,直到到达表达式的未尾,此时,数字栈中只剩表达式的值
          • 示例
            • 技术分享图片
          • 时间复杂度为 O(n)
        • 将中缀表达式转换为后缀表达式
          • 步骤
            • 开始时,有一个空的后缀表达式和一个空的栈,栈用来保存运算符和左圆括号
            • 从左到右,扫描中缀表达式
            • 遇到一个运算数时,将其添加到后缀表达式中
            • 遇到一个左圆括号时,将其压入栈中
            • 遇到一个运算符,从栈中弹出和它具有相等或更高优先级的所有运算符,并将其依次添加到后缀表达式中
            • 遇到一个右圆括号时,将运算符从栈中移动到后缀表达式中,直到碰到与之匹配的左圆括号,并将其丢弃
            • 遇到中缀表达式的结束时,将栈中剩余的运算符全部转移到后缀表达式中
          • 示例
            • 技术分享图片
            • 技术分享图片
          • 时间复杂度为 O(n)
        • 回溯算法
          • 描述
            • 回溯算法从一个预定义的起始状态开始,随后从一个状态移动到另一个状态,以搜索想要的最终状态。
            • 在任何状态下,如果有多个选择,则会随机选取一种状态,然后继续
            • 如果算法到达了不希望结果的一个状态,它会回到上一个拥有一个末探索的、可替代选项的位置,并尝试这个可替代选项。
            • 最后,要么算法到达了想要的结果,要么穷尽了对所有算法的搜索
          • 栈的作用是在每一个关头记住可替代的状态
          • 伪代码
            • Create an empty stack

              Push the starting state onto the stack

              while the stack in not empty:

                  Pop the stack and eaxmine the state

                  if the state represents an ending state

                      return SUCCESSFUL CONCLUSION

                  elif the state hasn‘t been visited previously

                      Mark the state as visited

                      Push onto the stack all unvisited adjacent states.

              return UNSUCCESSFUL CONCLUSION

          • 算法整体复杂度为 O(n)
        • 内存管理
          • PVM( Python Virtual Machine ) 运行时的环境架构
            • 技术分享图片
            • locationCounter 指向 PVM 下一步将要执行的指令
            • basePtr指向基本活动记录的顶部
          • 调用子例程的步骤
            • 创建子例程的活动记录,并将其压入栈
              • 在标记为 Prev basePtr 的区域中保存 basePtr 的当前值,并将 basePtr 设置为新的活动记录位置
              • Retrun Address 中,保存 locationCounter 的当前值,并将 locationCounter 设置为被调用子例程的第1条指令
              • 将调用参数复制到 Parameters 的区域中
              • 开始执行位于 locationCounter 所指示位置的子程序
            • 在子例程执行的过程中,通过给 basePtr 加上一个偏移量,以引用活动记录中的临时变量和参数
            • 在返回之前,一个子例程将其返回值存储在Return Value 的位置。
            • 当子例程执行完成后,PVM执行以下操作:
              • 使用活动记录中存储的值来恢复locationCounter basePtr 的值,从而重新建立调用子例程所需的设置
              • 从调用栈中弹出活动记录
              • locationCounter 所指示的位置,继续指定调用子例程
      • 栈的实现
        • 测试驱动程序
          • 代码
            • #!/usr/bin/env python

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

              # Author:Lijunjie

               

              """

              File: teststack.py

              A tester program for stack implementations.

              """

               

              # from arraystack import ArrayStack

              from linkedstack import LinkedStack

               

              def test( stackType ):

                  """Test any implementation with the same code."""

                  s = stackType()

                  print( "Length: ", len( s ) )

                  print( "Empty: ", s.isEmpty )

                  print( "Push 1-10" )

                  for i in range( 10 ):

                      s.push( i + 1 )

                  print( "Peeking: ", s.peek() )

                  print( "Item ( bottom to top ): ", s )

                  print( "Length: ", len( s ) )

                  print( "Empty: ", s.isEmpty )

                  theClone = stackType( s )

                  print( "Items in clone ( bottom to top ): ", theClone )

                  theClone.clear()

                  print( "Length of clone after clear: ", len( theClone ) )

                  print( "Push 11" )

                  s.push( 11 )

                  print( "Poping items( top to bottom):", end = "" )

                  while not s.isEmpty():print( s.pop(), end = " " )

                  print( "\nLength: ", len( s ) )

                  print( "Empty: ", s.isEmpty() )

                  print( "Test precondition of pop." )

                  try:

                      s.pop()

                  except KeyError:

                      print( "KeyError" )

                  print( "Test precondition of peek." )

                  try:

                      s.peek()

                  except KeyError:

                      print( "KeyError" )

                 

                 

              # test( ArrayStack )

              test( LinkedStack )

 

      • 将栈添加到集合层级中
        • 集合层级中的栈资源
          • 技术分享图片
      • 数组实现
        • 当数组填满或者装填因子小于 1/4 时,需要对数组的容量进行调整
        • 代码
          • ##!/usr/bin/env python

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

            # Author:Lijunjie

             

            from abstractstack import AbstractStack

            from arrays import Array

             

            class ArrayStack( AbstractStack ):

                """An array-based stack."""

               

                #Class variable

                DEFAULT_CAPACITY = 10

               

               

                def __init__( self, sourceCollection = None ):

                    """Sets the initial state of self, which includes the contents

                    of sourceCollection, if it‘s present."""

                    self._items = Array( ArrayStack.DEFAULT_CAPACITY )

                    AbstractStack.__init__( self, sourceCollection )

               

               

                # Accessor method

                def __iter__( self ):

                    """Support iteration over a view of self.

                    From bottom to top."""

                    cursor = 0

                    while cursor < len( self ):

                        yield self._items[ cursor ]

                        cursor += 1

                       

               

                def peek( self ):

                    """Returns the items at top of the stack

                    Precondition: stack is not empty.

                    Raise: KeyError if stack is empty"""

                    if self.isEmpty():

                        raise KeyError( "The stack is empty." )

                    return self._items[len(self) - 1]

                   

                # Mutator method

                def clear( self ):

                    """Clear the stack."""

                    self._items = Array( ArrayStack.DEFAULT_CAPACITY )

                    self._size = 0

                   

                   

                def push( self, item ):

                    """Push an item at the top of the stack"""

                    # Resize the array if necessary.

                    self.grow()

                    self._size += 1

                    self._items[len(self) - 1] = item

                   

                   

                def pop( self ):

                    """ Returns and remove the item at the top of the stack.

                    precondition: the stack is not empty.

                    raise: raise KeyError if the stack is empty."""

                    if self.isEmpty():

                        raise KeyError( "The stack is empty." )

                    oldItem = self._items[ len(self) - 1 ]

                    self._size -= 1

                    # Resize the array if necessary.

                    self.shrink()

                    return oldItem

                   

                   

                def grow( self ):

                    """Grow the capacity of the array if necessary """

                    physicalSize = len( self._items )

                    if len( self ) >= physicalSize:

                        temp = Array( physicalSize * 2 )

                        index = 0

                        for item in self:

                            temp[index] = item

                            index += 1

                        self._items = temp

               

             

                def shrink( self ):

                    """Shrink the capacity of the array if necessary. """

                    physicalSize = len( self._items )

                    if len( self ) <= physicalSize // 4 and physicalSize >= 2 * ArrayStack.DEFAULT_CAPACITY:

                        temp = Array( physicalSize//2 )

                        index = 0

                        for item in self:

                            temp[index] = item

                            index += 1

                        self._items = temp

      • 链表实现
        • 在链表的头部实现压入和弹出操作
        • 为了实现从底部向顶部遍历,需要使用迭代方法
        • 代码
          • ##!/usr/bin/env python

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

            # Author:Lijunjie

             

            from abstractstack import AbstractStack

            from node import Node

             

            class LinkedStack( AbstractStack ):

                """An linked-based stack."""

               

               

                def __init__( self, sourceCollection = None ):

                    """Sets the initial state of self, which includes the contents

                    of sourceCollection, if it‘s present."""

                    self._items = None

                    AbstractStack.__init__( self, sourceCollection )

               

               

                # Accessor method

                def __iter__( self ):

                    """Support iteration over a view of self.

                    From bottom to top."""

                    def visitNode( node ):

                        if node != None:

                            visitNode( node.next )

                            tempList.append( node.data )

                    tempList = list()

                    visitNode( self._items )

                    return( iter( tempList ) )

             

                   

                def peek( self ):

                    """Returns the items at top of the stack

                    Precondition: stack is not empty.

                    Raise: KeyError if stack is empty"""

                    if self.isEmpty():

                        raise KeyError( "The stack is empty." )

                    return self._items.data

                   

                # Mutator method

                def clear( self ):

                    """Clear the stack."""

                    self._items = None

                    self._size = 0

                   

                   

                def push( self, item ):

                    """Push an item at the top of the stack"""

                    # Resize the array if necessary.

                    self._size += 1

                    self._items = Node( item, self._items )

                   

                   

                def pop( self ):

                    """ Returns and remove the item at the top of the stack.

                    precondition: the stack is not empty.

                    raise: raise KeyError if the stack is empty."""

                    if self.isEmpty():

                        raise KeyError( "The stack is empty." )

                    oldItem = self._items.data

                    self._items = self._items.next

                    self._size -= 1

                    return oldItem

             

      • AbstractStack 类的作用
        • 定义 add 类,以避免修改AbstractCollection
        • 代码
          • ##!/usr/bin/env python

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

            # Author:Lijunjie

             

            """

            File name: abstractstack.py

            """

             

            from abstractcollection import AbstractCollection

             

            class AbstractStack( AbstractCollection ):

                """ An abstract stack class """

               

                #Constructor

                def __init__( self, sourceCollection = None ):

                    """Sets the initial state of self, which includes the contents

                    of sourceCollection, if it‘s present."""

                    AbstractCollection.__init__( self, sourceCollection )

                   

                # Mutator method

                def add( self, item ):

                    """Add item to self"""

                    self.push( item )

 

      • 两种实现的时间和空间分析
        • 时间性能
          • 除了 __iter__ 方法,所有的栈方法的运行时间均不大于 O(1)
            • 数组实现,在数组容量翻倍或者减半时,运行时间会增加到 O(n)
              • 使用数组栈时,需要决定响应时间上的波动是否可以接受
          • __iter__方法都是线性时间运行的
        • 空间性能
          • 链表实现使用了迭代,由于系统调用栈,从而导致内存线性增长
          • 当数组的填充因子大于 1/2 时,数组的内存性能优于链表

 

 

      • 案例学习:计算后缀表达式
        • 要求
          • 编写交互式程序,来计算后缀表达式
        • 分析
          • 用户交互
            • 用户在提示符下输入一个表达式,程序显示结果。输入表达式时,限制在一行文本之内,标记之间可以有任意空白。用户按下Enter Return键后,按照每个标记之间只有一个空格的形式输出表达式。并且在后面新的一行中,输出表达式的值,或者是错误信息加上当前运行状态。
            • 可能包含的错误
              • 表达式包含了太多的运算数
              • 表达式包含的运算数不够
              • 表达式包含了不识别的标志
              • 表达式包含了除以0的情况
        • 设计
          • 计算程序的交互图
            • 技术分享图片
          • PFEvaluatorView类的实例变量和方法
            • PFEvaluatorView()

                  Create and saves a reference to the model.

                 

                  run()

                      while True:

                          Retrieve the expressions string from keyboard.

                          Send it to the model for fromating and print the format string.

                          Send it to the model for evaluation.

                          Either print the value or catch exceptions raised by the evaluator.

                          ask the model for the associated details, and display error.

                          messages.          

               

          • PFEvaluatorModel 类的实例变量和方法
            • Model 模型需要与扫描程序和计算程序进行通信。
            • 方法如下:
              • format( expressionStr ):

                    Instantiate a scanner on the expression string.

                    Build a response string by iterating acorss the scanner and appending a

                    string representation of each token to the response string.

                    Return the reponse string.

                   

                 

                evaluate( expressionStr ):

                    Ask the evaluator to evaluate the expression string.

                    Return the value.

                   

                   

                evaluationStatus():

                    Ask the evaluator for its status.

                    Return the status

                 

          • PFEvaluator 类的实例变量和方法
            • 计算程序的属性包括一个栈,一个扫描程序和一个名为 expressionSoFar的字符串变量
            • 方法:
              • PFEvaluator( scanner )

                    Intialize expressionSoFar

                    Instantiate an ArrayStack

                    Save a reference to the scanner

                   

                   

                evaluate()

                    Iterate across the scanner and evaluate the expression.

                    Raise exception in the following situation:

                        The scanner is None or empty

                        There are too many operands

                        There are too few operands

                        There are unrecognizable tokens.

                        A divide by 0 exception is raised by the PVM

                   

                   

                evaluationStatus()

                    Return a multipart stirng that contains the portion of the expression

                    processed and the contents of the stack.

                 

          • Scanner 类的实例变量和方法
            • Scanner( sourceStr ):

                  Save a reference to the string that will be scanned and tokenized

                 

               

              hasNext()

                  Return True if the string contains another token and False otherwise.

                 

              next()

                  Return the next token. Raise an exception if hasnext() return False.

               

          • Token 类的实例变量与方法
            • Type定义
              • UNKNOWN = 0     #unknow

                INT = 4         #interger

                MINUS = 5       #minus operator

                PLUS = 6        #plus operator

                MUL = 7         #multiply operator

                DIV = 8         #divide operator

                 

            • 方法
              • Token( vlaue ):

                    Construct a new integer token with the specified value

                   

                Token(ch):

                    if ch is an operator( + - * / ), then construct a new operator token;

                    otherwise, construct a token of unknow type.

                   

                 

                getType()

                    return a token‘s type

                   

                getValue()

                    return a token‘s value.

                   

                isOperator():

                    Return True if the token is an operator, and False Otherwise

                   

                __str__():

                    Retrun the token‘s numeric value as a string if the token is an

                    integer; otherwise, return the token‘s character representation.

                 

 

第7章:栈

标签:ddl   initial   ext   family   nta   index   bee   plist   替代   

原文地址:https://www.cnblogs.com/lijunjie9502/p/9892559.html

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