一、雙端隊列
雙端隊列
雙端隊列(deque,全名double-ended queue),是一種具有隊列和棧的性質的數據結構。
雙端隊列中的元素可以從兩端彈出,其限定插入和刪除操作在表的兩端進行。雙端隊列可以在隊列任意一端入隊和出隊。
雙端隊列(Deque),是一種類似於隊列的元素的有序集合。它擁有兩端,隊首和隊尾,並且元素保持在當前的位置。雙端隊列的一個不同點就是,添加和刪除元素的位置不受限制。新元素可以在隊首或者隊尾添加。同樣地,雙端隊列中的元素可以從兩端彈出。在某種意義上,這種混合的線性結構同時具有棧和隊列的性質。
方法
-
Deque() 創建一個空的雙端隊列
-
add_front(item) 從隊頭加入一個item元素
-
add_rear(item) 從隊尾加入一個item元素
-
remove_front() 從隊頭刪除一個item元素
-
remove_rear() 從隊尾刪除一個item元素
-
is_empty() 判斷雙端隊列是否為空
-
size() 返回隊列的大小
Python實現雙端隊列
在Python中,有兩種方式可以實現上述的雙端隊列ADT:使用內建類型list、使用標准庫collections.deque(其實collections.deque就是Python中雙端隊列的標准實現)。
兩種方式的不同主要體現在性能上(具體參考 collections.deque | TimeComplexity):
用列表實現雙端隊列
class Deque(object): """雙端隊列""" def __init__(self): self.items = [] def is_empty(self): """判斷隊列是否為空""" return self.items == [] def add_front(self, item): """在隊頭添加元素""" self.items.insert(0,item) def add_rear(self, item): """在隊尾添加元素""" self.items.append(item) def remove_front(self): """從隊頭刪除元素""" return self.items.pop(0) def remove_rear(self): """從隊尾刪除元素""" return self.items.pop() def size(self): """返回隊列大小""" return len(self.items) if __name__ == "__main__": deque = Deque() deque.add_front(1) deque.add_front(2) deque.add_rear(3) deque.add_rear(4) print('隊列長度',deque.size()) print(deque.remove_front()) print(deque.remove_front()) print(deque.remove_rear()) print(deque.remove_rear()) print('隊列長度',deque.size())
分析:list作為容器,在雙端隊列的任何一端執行添加(append)和刪除(pop)操作,時間復雜度為O(1),
但是當list遇到pop(0)和insert(0, v)這樣既改變了列表的長度又改變其元素位置的操作時,其時間復雜度變為O(n)了。
使用標准庫collections.deque
Python的deque模塊,它是collections庫的一部分。deque實現了雙端隊列,意味着你可以從隊列的兩端加入和刪除元素。
append(x):把元素x添加到隊列的右端 appendleft(x):把元素x添加到隊列的左端 pop():移除並返回deque右端的元素,如果沒有元素拋IndexError popleft():移除並返回deque左端的元素,如果沒有元素拋IndexError index(x[, start[, stop]]):返回x元素在隊列中的索引,放回第一個匹配,如果沒有找到拋ValueError insert(i, x):在隊列的i索引處,插入x元素 remove(value):刪除第一個匹配value的元素,如果沒有找到拋ValueError clear():清空隊列中所有元素 copy():創建隊列的淺拷貝 count(x):計算隊列中等於x元素的個數 extend(iterable):在隊列右端通過添加元素擴展 extendleft(iterable):在隊列左端通過添加元素擴展 reverse():在原地反轉隊列中的元素 rotate(n):把隊列左端n個元素放到右端,如果為負值,右端到左端。如果n為1,等同d.appendleft(d.pop()) maxlen:只讀屬性,隊列中的最大元素數
簡單示例
from collections import deque # 實例化一個deque對象 d = deque() # 在隊列尾插入元素 d.append(1) d.append(2) d.append(3) d.append(4) print(d) #deque([1, 2, 3, 4]) # 在隊列頭插入元素 d.appendleft(5) print(d) # deque([5, 1, 2, 3, 4]) # 長度 print(len(d)) #5 # 隊列按索引取值 print(d[0]) #5 print(d[-1]) #4 print('-'*10) #在右側刪除一個元素 d.pop() print(d) # deque([5, 1, 2, 3]) # 在左側刪除一個元素 d.popleft() print(d) # deque([1, 2, 3]) # 擴展元素,類似列表的擴展,直接通過列表進行擴展,在deque右端插入多個元素 d.extend([4,5,6]) print(d) # deque([1, 2, 3, 4, 5, 6]) # 擴展元素,在deque左端插入多個元素,注意最后插入元素的順序與插入的列表相反 d.extendleft([7,8,9]) print(d) # deque([9, 8, 7, 1, 2, 3, 4, 5, 6])
也可以重寫,實現用列表方式調用形式的統一
# 使用標准庫collections.deque from collections import deque class Deque: def __init__(self): self.items = deque() def addFront(self, item): self.items.appendleft(item) def addRear(self, item): self.items.append(item) def removeFront(self): return self.items.popleft() def removeRear(self): return self.items.pop() def is_empty(self): return self.size() == 0 def size(self): return len(self.items)
deque各操作時間復雜度
注意:pop/popleft/append/appendleft 四種操作的時間復雜度均是 O(1)。
二、排序
排序算法(英語:Sorting algorithm)是一種能將一串數據依照特定順序進行排列的一種算法。
排序算法分類
1.比較類排序 通過比較來決定元素間的相對次序,由於其時間復雜度不能突破O(nlogn),因此也稱為非線性時間比較類排序
2.非比較類排序(一般用於整型相關的數據類型) 不通過比較來決定元素間的相對次序,它可以突破基於比較排序的時間下界,以線性時間運行,因此也稱為線性時間非比較類排序。
排序算法的穩定性
穩定性:穩定排序算法會讓原本有相等鍵值的紀錄維持相對次序。也就是如果一個排序算法是穩定的,當有兩個相等鍵值的紀錄R和S,且在原本的列表中R出現在S之前,在排序過的列表中R也將會是在S之前。
穩定性的意思是對於序列中鍵值(Key value)相同的元素,它們在排序前后的相對關系保持不變。對於int這樣的基本數據類型,穩定性基本上是沒有意義的,因為它的鍵值就是元素本身,兩個元素的鍵值相同他們就可以被認為是相同的。但對於復雜的數據類型,數據的鍵值相同,數據不一定相同,比如一個Student類,包括Name和Score兩個屬性,以Score為鍵值排序,這時候鍵值相同元素間的相對關系就有意義了。
當相等的元素是無法分辨的,比如像是整數,穩定性並不是一個問題。然而,假設以下的數對將要以他們的第一個數字來排序。
(4, 1) (3, 1) (3, 7)(5, 6)
在這個狀況下,有可能產生兩種不同的結果,一個是讓相等鍵值的紀錄維持相對的次序,而另外一個則沒有:
(3, 1) (3, 7) (4, 1) (5, 6) (維持次序)
(3, 7) (3, 1) (4, 1) (5, 6) (次序被改變)
不穩定排序算法可能會在相等的鍵值中改變紀錄的相對次序,但是穩定排序算法從來不會如此。不穩定排序算法可以被特別地實現為穩定。做這件事情的一個方式是人工擴充鍵值的比較,如此在其他方面相同鍵值的兩個對象間之比較,(比如上面的比較中加入第二個標准:第二個鍵值的大小)就會被決定使用在原先數據次序中的條目,當作一個同分決賽。然而,要記住這種次序通常牽涉到額外的空間負擔。
排序算法復雜度
初級排序(O(n^2)時間復雜度)
1.選擇排序
每次找最小值,然后放到待排序數組的起始位置
2.插入排序 從前到后 逐步構建有序序列;對於未排序的數列,在已排序序列中從后向前掃描,找到相應位置並插入。
3.冒泡排序 嵌套循環,每次查看相鄰元素,如果逆序,那么交換、
高級排序(O(nlogn)時間復雜度,重點考察)
-
快速排序 數組取標桿pivot,將小元素放pivot左邊,大元素放右邊,然后,依次對左邊和右邊的子數組繼續快排;以到達這個序列有序。
-
歸並排序(分治思想) 1.把長度為n的輸入序列分成兩個長度為n/2的子序列 2.對這兩個子序列分別采用歸並排序 3.將兩個排序好的子序列合並成一個最終的排序序列。
歸並與快排具有相似性,但步驟順序相反。
歸並:先排序左右子數組,然后合並兩個有序子數組 快排:先調配出左右子數組,然后對於左右子數組進行排序
-
堆排序
-
堆插入O(logN),取最大/小值O(1) 1.數組元素依次建立小頂堆 2.依次取棧頂元素,並刪除
-
其他排序(了解)
-
計數排序 計數排序要求輸入的數據必須是有確定范圍的整數。將輸入的數據值轉化為鍵存儲在額外開辟的數組空間中;然后依次把計數大於1的填充回原數組。
-
桶排序 桶排序的工作原理:假設輸入數據服從均勻分布,將數據分到有限數量的桶里面,每個桶在分別排序。
-
基數排序 基數排序是按照低為先排序,然后收集,再按照高位排序,然后再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序的,再按高優先級排序。
參考資料