前文傳送門:
引言
前一篇內容我們介紹了數組和向量,雖然說向量是數組的一個升級版,但是在另一個維度上,他們都屬於線性結構。
那么什么是線性結構呢?
線性結構是一個有序數據元素的集合。常用的線性結構有:線性表,棧,隊列,雙隊列,數組,串。
線性結構是最常用的數據結構,它最大的特點是數據元素之間存在一對一的線性關系。
線性結構擁有兩種不同的存儲結構,即順序存儲結構和鏈式存儲結構。
順序存儲的線性表稱為順序表,順序表中的存儲元素是連續的。
鏈式存儲的線性表稱為鏈表,鏈表中的存儲元素不一定是連續的,元素節點中存放數據元素以及相鄰元素的地址信息。
線性結構中存在兩種操作受限的使用場景,就是我們本文要介紹的棧和隊列。
至於為什么說棧和隊列是受限的線型結構,我們下面細聊。
棧
棧是一種比較奇葩的數據結構,棧的結構是支持對象的插入和刪除操作,但是,棧操作的范圍僅限於棧的某一特定端,就是下面這樣的。
棧遵循先進后出( last-in-first-out, LIFO )的規律,這是重點。
棧一般使用兩種方式來實現:
- 順序表:采用順序存儲結構可以模擬棧存儲數據的特點,從而實現棧存儲結構。
- 鏈表:采用鏈式存儲結構實現棧結構。
注意,這兩種實現方式的區別,僅限於數據元素在實際物理空間上存放的相對位置,順序棧底層采用的是數組,鏈棧底層采用的是鏈表。
棧結構我們還是會經常用到,一個非常經典的場景就是在瀏覽器的后退功能中。
例如我們每次打開一個頁面,瀏覽器都會把這個頁面放入棧中,當我們點擊后退按鈕的時候,在從棧中將這個頁面取出來。
順序棧
順序棧,是用順序表實現棧存儲結構。
棧存儲結構操作數據元素必須遵守 「先進后出 LIFO 」 的原則。
順序表的底層是使用數組來實現的,簡單理解可以直接理解成數組。
只是棧結構對數據的存取過程有特殊的限制,而數組是沒有的。
鏈棧
鏈棧,是用鏈表實現棧存儲結構。
鏈表這個結構在前面沒聊過,簡單畫個圖大家理解下:
鏈表的結構相比較數組而言就稍微有些復雜了,鏈表的每個節點由兩部分組成,一個是存放數據的,叫數據域,另一個是存放指針的,叫指針域。
數組在內存中是連續的,所以我們可以輕松的知道數組的每一個元素的位置,而鏈表在內存中是分散的,我們需要一個指針來指明下一個元素在哪里。
這里介紹的其實是最簡單的一種鏈表,叫單鏈表,顧名思義,除了單鏈表之外還有雙鏈表,這個我們有機會后面再聊。
那么鏈棧是將鏈表的頭部作為棧頂,尾部作為棧底。
將鏈表頭部作為棧頂的一端,可以避免在實現數據 「入棧」 和 「出棧」 操作時做大量遍歷鏈表的耗時操作。
鏈表的頭部作為棧頂,意味着:
- 在實現數據"入棧"操作時,需要將數據從鏈表的頭部插入。
- 在實現數據"出棧"操作時,需要刪除鏈表頭部的首元節點。
因此,鏈棧實際上就是一個只能采用頭插法插入或刪除數據的鏈表。
Python 實現棧
在 Python 中,棧並不是一個基礎數據結構,不過我們可以通過代碼來簡單的實現它。
因為棧是可以通過兩種方式來實現,一種是順序表,另一種是鏈表:
首先是最簡單的通過順序表來實現棧,這里使用的是 Python 中的 list 列表:
class Stack(object):
def __init__(self):
'''
創建空列表實現棧
'''
self.__list = []
def is_empty(self):
'''
判斷是否為空
:return:
'''
return self.__list == []
def push(self,item):
'''
壓棧,添加元素
:param item:
:return:
'''
self.__list.append(item)
def pop(self):
'''
彈出棧,將元素取出
:return:
'''
if self.is_empty():
return
else:
return self.__list.pop()
如果不想使用順序表來實現,還可以使用鏈表,這里使用的是單鏈表,鏈表的結構需要先提前定義:
class Node(object):
'''
節點實現
'''
def __init__(self,elem):
self.elem = elem
self.next = None
class Stack(object):
def __init__(self):
'''
初始化鏈表頭
'''
self.__head = None
def is_empty(self):
return self.__head is None
def push(self, item):
'''
壓棧
:param item:
:return:
'''
node = Node(item)
node.next = self.__head
self.__head = node
def pop(self):
'''
彈出棧
:return:
'''
if self.is_empty():
return
else:
p = self.__head
self.__head = p.next
return p.elem
在鏈表的實現中,我這里先定義了鏈表的數據結構,然后才定義了棧。
上面兩段代碼都非常簡單,只實現了最簡單的兩個功能,入棧和出棧,感興趣的同學可以自己動手實現下。
隊列
與棧一樣,隊列( queue) 也是存放數據對象的一種容器,其中的數據對象也按線性的邏輯次序排列。
隊列和棧不一樣的地方在於棧是先進后出,而隊列是先進先出( first-in-first-out, FIFO )。
同棧一樣的是隊列也有兩種實現方式:
- 順序隊列:在順序表的基礎上實現的隊列結構。
- 鏈隊列:在鏈表的基礎上實現的隊列結構。
Python 中的 Queue
在 Python 的標准庫中,Python 為我們提供了線程安全的隊列 Queue (總算不用我再自己寫個隊列了),使用方法異常簡單:
先進先出隊列 (FIFO) :
import queue
q1 = queue.Queue(maxsize=5)
for i in range(5):
q1.put(i)
while not q1.empty():
print('q1:',q1.get())
# 結果輸出
q1: 0
q1: 1
q1: 2
q1: 3
q1: 4
Queue 這個標准庫中,還為我們提供了 LIFO 隊列,即先進后出隊列,和我們前面介紹的棧非常類似,翻了下源碼,看到是使用 list 實現的,和我們上面的實現基本一致,使用方式如下:
import queue
q2 = queue.LifoQueue(maxsize=5)
for i in range(5):
q2.put(i)
while not q2.empty():
print('q2:',q2.get())
# 結果輸出
q2: 4
q2: 3
q2: 2
q2: 1
q2: 0
本篇內容就這樣了,涉及到的代碼肯定會上傳代碼倉庫,有興趣的同學可以去翻翻看。