一旦一個數據項被添加,它相對於前後元素一直保持該位置不變。諸如此類的數據結構被稱爲線性數據結構。棧,隊列,deques, 列表是一類數據的容器,它們數據項之間的順序由添加或刪除的順序決定。
線性數據結構有兩端,有時被稱爲左右,某些情況被稱爲前後。你也可以稱爲頂部和底部,名字都不重要。將兩個線性數據結構區分開的方法是添加和移除項的方式,特別是添加和移除項的位置。例如一些結構允許從一端添加項,另一些允許從另一端移除項。
棧
棧(有時稱爲“後進先出棧”)是一個項的有序集合,其中添加移除新項總髮生在同一端。這一端通常稱爲“頂部”。與頂部對應的端稱爲“底部”。
棧的底部很重要,因爲在棧中靠近底部的項是存儲時間最長的。最近添加的項是最先會被移除的。這種排序原則有時被稱爲 LIFO,後進先出。它基於在集合內的時間長度做排序。較新的項靠近頂部,較舊的項靠近底部。
棧的例子很常見。幾乎所有的自助餐廳都有一堆托盤或盤子,你從頂部拿一個,就會有一個新的托盤給下一個客人。Figure 1 展示了一個棧,包含了很多 Python 對象。
Figure 1
和棧相關的最有用的想法之一來自對它的觀察。假設從一個乾淨的桌面開始,現在把書一本本疊起來,你在構造一個棧。考慮下移除一本書會發生什麼。移除的順序跟剛剛被放置的順序相反。棧之所以重要是因爲它能反轉項的順序。插入跟刪除順序相反,Figure 2 展示了 Python 數據對象創建和刪除的過程,注意觀察他們的順序。
Figure 2
想想這種反轉的屬性,你可以想到使用計算機的時候所碰到的例子。例如,每個 web 瀏覽器都有一個返回按鈕。當你瀏覽網頁時,這些網頁被放置在一個棧中(實際是網頁的網址)。你現在查看的網頁在頂部,你第一個查看的網頁在底部。如果按‘返回’按鈕,將按相反的順序瀏覽剛纔的頁面。
棧的抽象數據類型
棧的抽象數據類型由以下結構和操作定義。如上所述,棧被構造爲項的有序集合,其中項被添加和從末端移除的位置稱爲“頂部”。棧是有序的 LIFO 。棧操作如下。
- Stack() 創建一個空的新棧。 它不需要參數,並返回一個空棧。
- push(item)將一個新項添加到棧的頂部。它需要 item 做參數並不返回任何內容。
- pop() 從棧中刪除頂部項。它不需要參數並返回 item 。棧被修改。
- peek() 從棧返回頂部項,但不會刪除它。不需要參數。 不修改棧。
- isEmpty() 測試棧是否爲空。不需要參數,並返回布爾值。
- size() 返回棧中的 item 數量。不需要參數,並返回一個整數。
例如,s 是已經創建的空棧,Table1 展示了棧操作序列的結果。棧中,頂部項列在最右邊。
Table 1
Python 代碼實現:
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self, item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
隊列
隊列抽象數據類型由以下結構和操作定義。如上所述,隊列被構造爲在隊尾添加項的有序集合,並且從隊首移除。隊列保持 FIFO 排序屬性。 隊列操作如下:
- Queue() 創建一個空的新隊列。 它不需要參數,並返回一個空隊列。
- enqueue(item) 將新項添加到隊尾。 它需要 item 作爲參數,並不返回任何內容。
- dequeue() 從隊首移除項。它不需要參數並返回 item。 隊列被修改。
- isEmpty() 查看隊列是否爲空。它不需要參數,並返回布爾值。
- size() 返回隊列中的項數。它不需要參數,並返回一個整數。
作爲示例,我們假設 q 是已經創建並且當前爲空的隊列,則 Table 1 展示了隊列操作序列的結果。右邊表示隊首。 4 是第一個入隊的項,因此它 dequeue 返回的第一個項。
Python實現隊列
我們爲了實現隊列抽象數據類型創建一個新類。和前面一樣,我們將使用列表集合來作爲構建隊列的內部表示。
我們需要確定列表的哪一端作爲隊首,哪一端作爲隊尾。Listing 1 所示的實現假定隊尾在列表中的位置爲 0。這允許我們使用列表上的插入函數向隊尾添加新元素。彈出操作可用於刪除隊首的元素(列表的最後一個元素)。回想一下,這也意味着入隊爲 O(n),出隊爲 O(1)。
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.insert(0,item)
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
deque雙端隊列
deque(也稱爲雙端隊列)是與隊列類似的項的有序集合。它有兩個端部,首部和尾部,並且項在集合中保持不變。deque 不同的地方是添加和刪除項是非限制性的。可以在前面或後面添加新項。同樣,可以從任一端移除現有項。在某種意義上,這種混合線性結構提供了單個數據結構中的棧和隊列的所有能力。
要注意,即使 deque 可以擁有棧和隊列的許多特性,它不需要由那些數據結構強制的 LIFO 和 FIFO 排序。這取決於你如何持續添加和刪除操作。
Deque抽象數據類型
deque 抽象數據類型由以下結構和操作定義。如上所述,deque 被構造爲項的有序集合,其中項從首部或尾部的任一端添加和移除。下面給出了 deque 操作。
- Deque() 創建一個空的新 deque。它不需要參數,並返回空的 deque。
- addFront(item) 將一個新項添加到 deque 的首部。它需要 item 參數 並不返回任何內容。
- addRear(item) 將一個新項添加到 deque 的尾部。它需要 item 參數並不返回任何內容。
- removeFront() 從 deque 中刪除首項。它不需要參數並返回 item。deque 被修改。
- removeRear() 從 deque 中刪除尾項。它不需要參數並返回 item。deque 被修改。
- isEmpty() 測試 deque 是否爲空。它不需要參數,並返回布爾值。
- size() 返回 deque 中的項數。它不需要參數,並返回一個整數。
例如,我們假設 d 是已經創建並且當前爲空的 deque,則 Table 1 展示了一系列 deque 操作的結果。注意,首部的內容列在右邊。在將 item 移入和移出時,跟蹤前面和後面是非常重要的,因爲可能會有點混亂。
Python實現Deque
class Deque:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def addFront(self, item):
self.items.append(item)
def addRear(self, item):
self.items.insert(0,item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
def size(self):
return len(self.items)
無序列表抽象數據類型
如上所述,無序列表的結構是項的集合,其中每個項保持相對於其他項的相對位置。下面給出了一些可能的無序列表操作。
- List() 創建一個新的空列表。它不需要參數,並返回一個空列表。
- add(item) 向列表中添加一個新項。它需要 item 作爲參數,並不返回任何內容。假定該 item 不在列表中。
- remove(item) 從列表中刪除該項。它需要 item 作爲參數並修改列表。假設項存在於列表中。
- search(item) 搜索列表中的項目。它需要 item 作爲參數,並返回一個布爾值。
- isEmpty() 檢查列表是否爲空。它不需要參數,並返回布爾值。
- size()返回列表中的項數。它不需要參數,並返回一個整數。
- append(item) 將一個新項添加到列表的末尾,使其成爲集合中的最後一項。它需要 item
作爲參數,並不返回任何內容。假定該項不在列表中。 - index(item) 返回項在列表中的位置。它需要 item 作爲參數並返回索引。假定該項在列表中。
- insert(pos,item) 在位置 pos 處向列表中添加一個新項。它需要 item
作爲參數並不返回任何內容。假設該項不在列表中,並且有足夠的現有項使其有 pos 的位置。 - pop() 刪除並返回列表中的最後一個項。假設該列表至少有一個項。
- pop(pos) 刪除並返回位置 pos 處的項。它需要 pos 作爲參數並返回項。假定該項在列表中。
Python實現無須鏈表
class Node:
def __init__(self, initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self, newdata):
self.data = newdata
def setNext(self, newdata):
self.next = newdata
class UnorderList:
def __init__(self):
self.head = None
def isEmpty(self):
return self.head == None
def add(self, item):
temp = Node(item)
temp.setNext(self.head)
self.head = temp
def size(self):
current = self.head
count = 0
while current != None:
count += 1
current = current.getNext()
return count
def search(self, item):
current = self.head
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
current = current.getNext()
return found
def remove(self, item):
current = self.head
previous = None
found = False
while not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if previous == None:
self.head = current.getNext()
else:
previous.setNext(current.getNext())
有序列表抽象數據結構
我們現在將考慮一種稱爲有序列表的列表類型。例如,如果上面所示的整數列表是有序列表(升序),則它可以寫爲 17,26,31,54,77和93。由於 17 是最小項,它佔據第一位置。同樣,由於 93 是最大的,它佔據最後的位置。
有序列表的結構是項的集合,其中每個項保存基於項的一些潛在特性的相對位置。排序通常是升序或降序,並且我們假設列表項具有已經定義的有意義的比較運算。許多有序列表操作與無序列表的操作相同。
- OrderedList() 創建一個新的空列表。它不需要參數,並返回一個空列表。
- add(item) 向列表中添加一個新項。它需要 item 作爲參數,並不返回任何內容。假定該 item 不在列表中。
- remove(item) 從列表中刪除該項。它需要 item 作爲參數並修改列表。假設項存在於列表中。
- search(item) 搜索列表中的項目。它需要 item 作爲參數,並返回一個布爾值。
- isEmpty() 檢查列表是否爲空。它不需要參數,並返回布爾值。
- size()返回列表中的項數。它不需要參數,並返回一個整數。
- index(item) 返回項在列表中的位置。它需要 item 作爲參數並返回索引。假定該項在列表中。
- pop() 刪除並返回列表中的最後一個項。假設該列表至少有一個項。
pop(pos) 刪除並返回位置 pos 處的項。它需要 pos 作爲參數並返回項。假定該項在列表中。
總結
- 線性數據結構以有序的方式保存它們的數據。
- 棧是維持 LIFO,後進先出,排序的簡單數據結構。
- 棧的基本操作是 push,pop和 isEmpty。
- 隊列是維護 FIFO(先進先出)排序的簡單數據結構。
- 隊列的基本操作是 enqueue,dequeue 和 isEmpty。
- 前綴,中綴和後綴都是寫表達式的方法。
- 棧對於設計計算解析表達式算法非常有用。
- 棧可以提供反轉特性。
- 隊列可以幫助構建定時仿真。
- 模擬使用隨機數生成器來創建真實情況,並幫助我們回答“假設”類型的問題。
- Deques 是允許類似棧和隊列的混合行爲的數據結構。
- deque 的基本操作是 addFront,addRear,removeFront,removeRear 和 isEmpty。
- 列表是項的集合,其中每個項目保存相對位置。
- 鏈表實現保持邏輯順序,而不需要物理存儲要求。
- 修改鏈表頭是一種特殊情況。