<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
棧由一系列物件物件組織的一個集合,這些物件的增加和刪除操作都遵循一個“後進先出”(Last In First Out,LIFO)的原則。
在任何時刻只能向棧中插入一個物件,但只能取得或者刪除只能在棧頂進行。比如由書構成的棧,唯一露出封面的書就是頂部的那本,為了拿到其他的書,只能移除壓在上面的書,如圖:
實際上很多應用程式都會用到棧,比如:
網路瀏覽器將最近瀏覽的網址存放在一個棧中。每當使用者存取者存取一個新網站時,這個新網站的網址就被壓入棧頂。這樣,每當我們在瀏覽器單擊“後退”按鈕時(或者按鍵盤快捷鍵 CTRL+Z ,大部分復原快捷鍵),就可以彈出當前最近一次存取的網址,以回到其先前存取的瀏覽狀態。
文字編輯器通常會提供一個“復原”機制以取消最近的編輯操作並返回到先前狀態。這個復原操作也是通過將文字的變化狀態儲存在一個棧中得以實現。
一些高階語言的記憶體管理,JVM 的棧、Python 棧還用於記憶體管理、巢狀語言特性的執行時環境等
回溯(玩遊戲,尋找路徑,窮舉搜尋)
在演演算法中使用,如漢諾塔、樹形遍歷、直方圖問題,也用於圖演演算法,如拓撲排序
語法處理:
任何資料結構都離不開資料的儲存和獲得方式,如前所述,棧是元素的有序集合,新增和操作與移除都發生在其頂端(棧頂),那麼它的抽象資料型別包括:
Stack()
:建立一個空棧,它不需要引數,且會返回一個空棧push(e)
: 將一個元素 e 新增到棧 S 的棧頂,它需要一個引數 e,且無返回值pop()
: 將棧頂端的元素移除,它不需要引數,但會返回頂端的元素,並且修改棧的內容top()
: 返回棧頂端的元素,但是並不移除棧頂元素;若棧為空,這個操作會操作is_empty()
: 如果棧中不包含任何元素,則返回一個布林值 True
size()
:返回棧中元素的資料。它不需要引數,且會返回一個整數。在 Python 中,可以用 __len__
這個特殊方法實現。Python 棧的大小可能是固定的,也可能有一個動態的實現,即允許大小變化。在大小固定棧的情況下,試圖向已經滿的棧新增一個元素會導致棧溢位異常。同樣,試圖從一個已經是空的棧中移除一個元素,進行 pop()
操作這種情況被稱為下溢。
在學習 Python 的時候,一定學過 Python 列表 list
, 它能通過一些內建的方式實現棧的功能:
append
方法用於新增一個元素到列表尾部,這種方式就能模擬 push()
操作pop()
方法用於模擬出棧操作L[-1]
模擬 top()
操作len(L)==0
模擬 isEmpty()
操作len()
函數實現 size()
函數程式碼如下:
class ArrayStack: """ 通過 Python 列表實現 LIFO 棧""" def __init__(self): self._data = [] def size(self): """ return the number of elements in the stack""" return len(self._data) def is_empty(self): """ return True if the stack is empty""" return len(self._data) == 0 def push(self, e): """ add element e to the top of the stack""" self._data.append(e) def pop(self): """ remove and return the element from the top of the stack """ if self.is_empty(): raise Exception('Stack is empty') return self._data.pop() def top(self): """return the top of the stack Raise Empty exception if the stack is empty """ if self.is_empty(): raise Exception('Stack is empty') return self._data[-1] # the last item in the list arrayStack = ArrayStack() arrayStack.push("Python") arrayStack.push("Learning") arrayStack.push("Hello") print("Stack top element: ", arrayStack.top()) print("Stack length: ", arrayStack.size()) print("Stack popped item: %s" % arrayStack.pop()) print("Stack is empty?", arrayStack.is_empty()) arrayStack.pop() arrayStack.pop() print("Stack is empty?", arrayStack.is_empty()) # arrayStack.pop()
執行該程式,結果:
Stack top element: Hello
Stack length: 3
Stack popped item: Hello
Stack is empty? False
Stack is empty? True
除了將列表的隊尾作為棧頂,也可以通過將列表的頭部作為棧的頂端。不過在這種情況下,便無法直接使用 pop()
方法和 append()
方法,但是可以通過 pop()
和 insert()
方法顯式地存取下標為 0 的元素,即列表的第一個元素,程式碼如下:
class ArrayStack: """ 通過 Python 列表實現 LIFO 棧""" def __init__(self): self._data = [] def size(self): """ return the number of elements in the stack""" return len(self._data) def is_empty(self): """ return True if the stack is empty""" return len(self._data) == 0 def push(self, e): """ add element e to the top of the stack""" self._data.insert(0, e) def pop(self): """ remove and return the element from the top of the stack """ if self.is_empty(): raise Exception('Stack is empty') return self._data.pop(0) def top(self): """return the top of the stack Raise Empty exception if the stack is empty """ if self.is_empty(): raise Exception('Stack is empty') return self._data[0] # the last item in the list
雖然我們改變了抽象資料型別的實現,卻保留了其邏輯特徵,這種能力體現了抽象思想。不管,雖然兩種方法都實現了棧,但兩者的效能方法有差異:
append()
和 pop()
方法的時間複雜度都是 O(1),常數級別操作insert(0)
和 pop(0)
的時間複雜度都是 O(n),元素越多就越慢。在 Python 中,collections
模組有一個雙端佇列資料結構 deque,這個資料結構同樣實現了 append()
和 pop()
方法:
>>> from collections import deque >>> myStack = deque() >>> myStack.append('Apple') >>> myStack.append('Banana') >>> myStack.append('Orange') >>> >>> myStack deque(['Apple', 'Banana', 'Orange']) >>> myStack.pop() 'Orange' >>> myStack.pop() 'Banana' >>> >>> len(myStack) 1 >>> myStack[0] 'Apple' >>> myStack.pop() 'Apple' >>> >>> myStack.pop() Traceback (most recent call last): File "<pyshell#13>", line 1, in <module> myStack.pop() IndexError: pop from an empty deque >>>
可能你可以看到 deque 和列表 list 對元素的操作差不多,那麼為什麼 Python 中有列表還增加了 deque 這一個資料結構呢?
那是因為,Python 中的列表建立在連續的記憶體塊中,意味著列表的元素是緊挨著儲存的。
這對一些操作來說非常有效,比如對列表進行索引。獲取 myList[3]
的速度很快,因為 Python 確切地知道在記憶體中尋找它的位置。這種記憶體佈局也允許切片在列表上很好地工作。
毗連的記憶體佈局是 list 可能需要花費更多時間來 .append()
一些物件。如果連續的記憶體塊已經滿了,那麼它將需要獲得另一個記憶體塊,先將整體 copy 過去,這個動作可能比一般的 .append()
操作花費更多的時間。
而雙端佇列 deque
是建立在一個雙連結串列的基礎上。在一個連結列表結構中,每個條目都儲存在它自己的記憶體塊中,並有一個對列表中下一個條目的參照。
雙連結串列也是如此,只是每個條目都有對列表中前一個和後一個條目的參照。這使得你可以很容易地在列表的兩端新增節點。
在一個連結列表結構中新增一個新的條目,只需要設定新條目的參照指向當前堆疊的頂部,然後將堆疊的頂部指向新條目。
然而,這種在棧上不斷增加和刪除條目的時間是有代價的。獲取 myDeque[3]
的速度要比列表慢,因為 Python 需要走過列表的每個節點來獲取第三個元素。
幸運的是,你很少想在棧上做隨機索引元素或進行列表切片操作。棧上的大多數操作都是 push
或 pop
。
如果你的程式碼不使用執行緒,常數時間的 .append()
和 .pop()
操作使 deque 成為實現 Python 棧的一個更好的選擇。
Python 棧在多執行緒程式中也很有用,我們已經學習了 list
和 deque
兩種方式。對於任何可以被多個執行緒存取的資料結構,在多執行緒程式設計中,我們不應該使用 list
,因為列表不是執行緒安全的。deque 的 .append()
和 .pop()
方法是原子性的,意味著它們不會被不同的執行緒干擾。
因此,雖然使用 deque 可以建立一個執行緒安全的 Python 堆疊,但這樣做會使你自己在將來被人誤用,造成競態條件。
好吧,如果你是多執行緒程式設計,你不能用 list
來做堆疊,你可能也不想用 deque
來做堆疊,那麼你如何為一個執行緒程式建立一個 Python 堆疊?
答案就在 queue
模組中:queue.LifoQueue。還記得你是如何學習到棧是按照後進先出(LIFO)的原則執行的嗎?嗯,這就是 LifoQueue 的 "Lifo "部分所代表的含義。
雖然 list 和 deque 的介面相似,但 LifoQueue 使用 .put()
和 .get()
來從棧中新增和刪除資料。
>>> from queue import LifoQueue >>> stack = LifoQueue() >>> stack.put('H') >>> stack.put('E') >>> stack.put('L') >>> stack.put('L') >>> stack.put('O') >>> stack <queue.LifoQueue object at 0x00000123159F7310> >>> >>> stack.get() 'O' >>> stack.get() 'L' >>> stack.empty() False >>> stack.qsize() 3 >>> stack.get() 'L' >>> stack.get() 'E' >>> stack.qsize() 1 >>> stack.get() 'H' >>> stack.get_nowait() Traceback (most recent call last): File "<pyshell#31>", line 1, in <module> stack.get_nowait() _queue.Empty >>> >>> stack.put('Apple') >>> stack.get_nowait() 'Apple'
與 deque 不同,LifoQueue 被設計為完全執行緒安全的。它的所有方法都可以線上程環境中安全使用。它還為其操作新增了可選的超時功能,這線上程程式中經常是一個必須的功能。
然而,這種完全的執行緒安全是有代價的。為了實現這種執行緒安全,LifoQueue 必須在每個操作上做一些額外的工作,這意味著它將花費更長的時間。
通常情況下,這種輕微的減速對你的整體程式速度並不重要,但如果你已經測量了你的效能,並行現你的堆疊操作是瓶頸,那麼小心地切換到 deque 可能是值得做的。
一般來說,如果你不使用多執行緒,你應該使用 deque
。如果你使用多執行緒,那麼你應該使用 LifoQueue
,除非你已經測量了你的效能,發現 push
和 pop
的速度的小幅提升會帶來足夠的差異,以保證維護風險。
你可以對列表可能很熟悉,但需要謹慎使用它,因為它有可能存在記憶體重新分配的問題。deque
和 list
的介面是相同的,而且 deque
沒有執行緒不安全問題。
本文介紹了棧這一資料結構,並介紹了在現實生活中的程式中如何使用它的情況。在文章的中,介紹了 Python 中實現棧的三種不同方式,知道了 deque
對於非多執行緒程式是一個更好的選擇,如果你要在多執行緒程式設計環境中使用棧的話,可以使用 LifoQueue
。
參考連結:
以上就是Python 棧實現的幾種方式及優劣詳解的詳細內容,更多關於Python 棧實現方式優劣的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45