算法漫游指北(第六篇)雙端隊列、排序算法分類、排序算法的穩定性、排序算法復雜度


一、雙端隊列

雙端隊列

雙端隊列(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的填充回原數組。

  • 桶排序 桶排序的工作原理:假設輸入數據服從均勻分布,將數據分到有限數量的桶里面,每個桶在分別排序。

  • 基數排序 基數排序是按照低為先排序,然后收集,再按照高位排序,然后再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序的,再按高優先級排序。

     

     

 

參考資料

[1]https://www.cnblogs.com/onepixel/p/7674659.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM