1. 程式人生 > >第7章:棧

第7章:棧

      • 棧概覽
        • 棧是線性集合,遵從後進先出原則( Last - in first - out LIFO )原則
        • 棧常用的操作包括壓入( push ) 和彈出( pop )
          • clip_image001
        • 棧的應用
          • 將中綴表示式轉換為字尾表示式,並且計算字尾表示式的值
          • 回溯演算法
          • 管理計算機記憶體以支援函式和方法呼叫
          • 支援應用程式中的撤消功能
          • 維護Web瀏覽器訪問連結的歷史記錄
      • 使用棧
        • 棧不是Python的內建型別,可以用列表代替,但是列表可以在任何位置插入,刪除,替換元素,這些違背了棧作為一種抽象資料型別的本意,因此需要為棧定義一種更為嚴格的介面
        • 棧介面
          • clip_image002
        • 初始化一個棧
          • 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 種應用
        • 計算算術表示式
          • 中綴表示式
            • 運算子位於兩個運算數之間
            • 計算涉及優先順序問題,有時需要括號來表示優先順序
          • 字尾表示式
            • 運算子緊跟在運算數之後
            • 沒有優先順序問題,一遇到運算子就立即運用
          • 示例
            • clip_image003
        • 計算字尾表示式
          • 步驟(需要一個數字棧
            • 從左到右的遍歷表示式,遇到運算數,則將其壓入數字棧中
            • 碰到一個運算子時,從數字棧中彈出兩個運算數,對其應用運算子,並將所得的結果壓入數字棧
            • 繼續遍歷,直到到達表示式的未尾,此時,數字棧中只剩表示式的值
          • 示例
            • clip_image004
          • 時間複雜度為 O(n)
        • 將中綴表示式轉換為字尾表示式
          • 步驟
            • 開始時,有一個空的字尾表示式和一個空的棧,棧用來儲存運算子和左圓括號
            • 從左到右,掃描中綴表示式
            • 遇到一個運算數時,將其新增到字尾表示式中
            • 遇到一個左圓括號時,將其壓入棧中
            • 遇到一個運算子,從棧中彈出和它具有相等或更高優先順序的所有運算子,並將其依次新增到字尾表示式中
            • 遇到一個右圓括號時,將運算子從棧中移動到字尾表示式中,直到碰到與之匹配的左圓括號,並將其丟棄
            • 遇到中綴表示式的結束時,將棧中剩餘的運算子全部轉移到字尾表示式中
          • 示例
            • clip_image005
            • clip_image006
          • 時間複雜度為 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 ) 執行時的環境架構
            • clip_image007
            • 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 )

 

      • 將棧新增到集合層級中
        • 集合層級中的棧資源
          • clip_image008
      • 陣列實現
        • 當陣列填滿或者裝填因子小於 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