快速排序、堆排序、歸併排序比較
- 快速排序是二叉查詢樹(二叉查詢樹)的一個空間最優化版本。不是循序地把資料項插入到一個明確的樹中,而是由快速排序組織這些資料項到一個由遞迴呼叫所隱含的樹中。這兩個演算法完全地產生相同的比較次數,但是順序不同。對於排序演算法的穩定性指標,原地分割槽版本的快速排序演算法是不穩定的。其他變種是可以通過犧牲效能和空間來維護穩定性的。
- 快速排序的最直接競爭者是堆排序(Heapsort)。堆排序通常比快速排序稍微慢,但是最壞情況的執行時間總是O(n log n)。快速排序是經常比較快,除了introsort變化版本外,仍然有最壞情況效能的機會。如果事先知道堆排序將會是需要使用的,那麼直接地使用堆排序比等待introsort再切換到它還要快。堆排序也擁有重要的特點,僅使用固定額外的空間(堆排序是原地排序),而即使是最佳的快速排序變化版本也需要Θ(log n)的空間。然而,堆排序需要有效率的隨機存取才能變成可行。
- 快速排序也與歸併排序(Mergesort)競爭,這是另外一種遞迴排序演算法,但有壞情況O(n log n)執行時間的優勢。不像快速排序或堆排序,歸併排序是一個穩定排序,且可以輕易地被採用在連結串列(linked list)和儲存在慢速訪問媒體上像是磁碟儲存或網路連線儲存的非常巨大數列。儘管快速排序可以被重新改寫使用在煉串列上,但是它通常會因為無法隨機存取而導致差的基準選擇。歸併排序的主要缺點,是在最佳情況下需要Ω(n)額外的空間。
- 快速排序和堆排序不穩定,歸併排序穩定。
來自 維基百科-快速排序<https://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F>
快速排序時間複雜度:
從一開始快速排序平均需要花費O(n log n)時間的描述並不明顯。但是不難觀察到的是分割槽運算,陣列的元素都會在每次迴圈中走訪過一次,使用O(n)的時間。在使用結合(concatenation)的版本中,這項運算也是O(n)。
在最好的情況,每次我們執行一次分割槽,我們會把一個數列分為兩個幾近相等的片段。這個意思就是每次遞迴呼叫處理一半大小的數列。因此,在到達大小為一的數列前,我們只要作log n次巢狀的呼叫。這個意思就是呼叫樹的深度是O(log n)。但是在同一層次結構的兩個程式呼叫中,不會處理到原來數列的相同部分;因此,程式呼叫的每一層次結構總共全部僅需要O(n)的時間(每個呼叫有某些共同的額外耗費,但是因為在每一層次結構僅僅只有O(n)個呼叫,這些被歸納在O(n)係數中)。結果是這個演算法僅需使用O(n log n)時間。
另外一個方法是為T(n)設立一個遞迴關係式,也就是需要排序大小為n的數列所需要的時間。在最好的情況下,因為一個單獨的快速排序呼叫牽涉了O(n)的工作,加上對n/2大小之數列的兩個遞迴呼叫,這個關係式可以是:
T(n) = O(n) + 2T(n/2)
解決這種關係式型態的標準數學歸納法技巧告訴我們T(n) = O(n log n)。
事實上,並不需要把數列如此精確地分割槽;即使如果每個基準值將元素分開為99%在一邊和1%在另一邊,呼叫的深度仍然限制在100log n,所以全部執行時間依然是O(n log n)。
然而,在最壞的情況是,兩子數列擁有大各為1和n-1,且呼叫樹(call tree)變成為一個n個巢狀(nested)呼叫的線性連串(chain)。第i次呼叫作了O(n-i)的工作量,且遞迴關係式為:
T(n) = O(n) + T(1) + T(n - 1) = O(n) + T(n - 1)
這與插入排序和選擇排序有相同的關係式,以及它被解為T(n) = O(n2)。
快速排序空間複雜度:
被快速排序所使用的空間,依照使用的版本而定。使用原地(in-place)分割槽的快速排序版本,在任何遞迴呼叫前,僅會使用固定的額外空間。然而,如果需要產生O(log n)巢狀遞迴呼叫,它需要在他們每一個儲存一個固定數量的資訊。因為最好的情況最多需要O(log n)次的巢狀遞迴呼叫,所以它需要O(log n)的空間。最壞情況下需要O(n)次巢狀遞迴呼叫,因此需要O(n)的空間。
然而我們在這裡省略一些小的細節。如果我們考慮排序任意很長的數列,我們必須要記住我們的變數像是left和right,不再被認為是佔據固定的空間;也需要O(log n)對原來一個n項的數列作索引。因為我們在每一個堆疊框架中都有像這些的變數,實際上快速排序在最好跟平均的情況下,需要O(log2 n)空間的位元數,以及最壞情況下O(n log n)的空間。然而,這並不會太可怕,因為如果一個數列大部分都是不同的元素,那麼數列本身也會佔據O(n log n)的空間位元組。
非原地版本的快速排序,在它的任何遞迴呼叫前需要使用O(n)空間。在最好的情況下,它的空間仍然限制在O(n),因為遞迴的每一階中,使用與上一次所使用最多空間的一半,且平均為O(2n)。
它的最壞情況是很恐怖的,需要O(n2)空間,遠比數列本身還多。如果這些數列元素本身自己不是固定的大小,這個問題會變得更大;舉例來說,如果數列元素的大部分都是不同的,每一個將會需要大約O(log n)為原來儲存,導致最好情況是O(n log n)和最壞情況是O(n2 log n)的空間需求。