1. 程式人生 > >各種排序演算法複雜度比較

各種排序演算法複雜度比較

寫在前面

筆試題目當中會出現各種排序演算法的比較,分為最好,最壞,平均情況的複雜度比較,在這裡總結一下。

主要內容

這裡寫圖片描述

最好情況

一般會這麼問:在各自最優條件下以下演算法複雜度最低的是

看清題目的要求是問在最優的條件下,所以插入排序和氣泡排序是最優的為o(n)的複雜度。

氣泡排序這裡為啥最好情況時o(n)?
氣泡排序的最壞和平均都是o(n*n),但是當原本的序列本身就是有序的時候,事實上可以新增一個標誌位就可以搞定這個問題:此時只需要一次遍歷就能知道結果。

public void bubbleSort(int arr[]) {
    boolean
didSwap; for(int i = 0, len = arr.length; i < len - 1; i++) { didSwap = false; for(int j = 0; j < len - i - 1; j++) { if(arr[j + 1] < arr[j]) { swap(arr, j, j + 1); didSwap = true; } } if(didSwap == false
) return; } }

快排最好的情況是,每次正好中分,複雜度為O(nlogn)。最差情況,複雜度為O(n^2),退化成氣泡排序

快速排序的優化

  • 方法一:固定基準元(基本的快速排序)
  • 方法二:隨機基準元
  • 方法三:三數取中
  • 方法四. 兩種優化的方法:優化一:當待排序序列的長度分割到一定大小後,使用插入排序。優化二:在一次分割結束後,可以把與Key相等的元素聚在一起,繼續下次分割時,不用再對與key相等元素分割。

STLsort的快排優化:

STL中的sort並非只是普通的快速排序,除了對普通的快速排序進行優化,它還結合了插入排序和堆排序。根據不同的數量級別以及不同情況,能自動選用合適的排序方法。當資料量較大時採用快速排序,分段遞迴。一旦分段後的資料量小於某個閥值,為避免遞迴呼叫帶來過大的額外負荷,便會改用插入排序。而如果遞迴層次過深,有出現最壞情況的傾向,還會改用堆排序。

STL sort 函式實現詳解

演算法複雜度一樣只是說明隨著資料量的增加,演算法時間代價增長的趨勢相同,並不是執行的時間就一樣,這裡面有很多常量引數的差別,即使是同樣的演算法,不同的人寫的程式碼,不同的應用場景下執行時間也可能差別很大。
快排的最壞時間雖然複雜度高,但是在統計意義上,這種資料出現的概率極小,而堆排序過程裡的交換跟快排過程裡的交換雖然都是常量時間,但是常量時間差很多。

堆排序和快排比較

堆排序的最壞時間複雜度是O(nlogn),平均時間複雜度是O(nlogn)。但是堆排序的時間常數比較大,因此從平均來看堆排序的時間複雜度反而是最差的。 同級別下時間常數最大。

一般情況下,快速排序效率要高於堆排序。因為堆排序的常數較大(不過也是1~2之間吧)。
快速排序的平均時間複雜度是O(1.39nlogn)。一般來說,除非有需要絕對保證不能出現O(n^2)的要求,不使用堆排
堆排序需要有效的隨機存取。
這是演算法硬傷,沒辦法的。因為每次取一個最大值和堆底部的資料(記為X)交換,重新篩選堆,把堆頂的X調整到位,有很大可能是依舊調整到堆的底部(堆的底部X顯然是比較小的數,才會在底部),然後再次和堆頂最大值交換,再調整下來。從上面看出,堆排序做了許多無用功

插入排序為什麼在小資料上好於快速排序

插入排序:

1.從第一個元素開始,該元素可以認為已經被排序
2.取出下一個元素,在已經排序的元素序列中從後向前掃描
3.如果該元素(已排序)大於新元素,將該元素移到下一位置
4.重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
5.將新元素插入到該位置中
6.重複步驟2

  • 沒有額外記憶體的申請和釋放開銷
  • 沒有遞迴棧的開銷(但對於非遞迴實現的呢?)

不過,更更關鍵的,當我們使用大O來描述演算法的複雜度的時候,是忽略常數項的。大O刻畫的是當資料規模無窮大的時候,演算法效能的趨勢。他只是一個趨勢,不是精確的時間。我們說O(nlogn)的演算法比O(n^2)的演算法快,是因為當n無窮大的時候,哪怕O(nlogn)的演算法是T = 1000000nlogn,而O(n^2)的演算法是T = 2n^2,總有一個n,會使得1000000nlogn < 2n^2,並且隨著n逐漸增大,這個差距越來越大(解方程,試試看這個n在哪裡?)。但是,當n比較小的時候,就不一定了比如n=8的時候,1000000nlogn = 24000000;而2n^2只有128

插入排序法就是一個常數項非常小的排序演算法,小於大多數排序。同時,對於有序(或者近乎有序)的資料,插入排序還可以進化成為O(n)的演算法(因為第二層迴圈可以提前終止),而小資料量的陣列,擁有更高的概率是有序的。所以,可以作為在資料量比較低的時候的一種優化手段

不同條件下,排序方法的選擇

(1)若n較小(如n≤50),可採用直接插入或直接選擇排序。
 當記錄規模較小時,直接插入排序較好;否則因為直接選擇移動的記錄數少於直接插人,應選直接選擇排序為宜。

(2)若檔案初始狀態基本有序(指正序),則應選用直接插人、冒泡或隨機的快速排序為宜;

(3)若n較大,則應採用時間複雜度為O(nlgn)的排序方法:快速排序、堆排序或歸併排序。
 快速排序是目前基於比較的內部排序中被認為是最好的方法,當待排序的關鍵字是隨機分佈時,快速排序的平均時間最短;
 堆排序所需的輔助空間少於快速排序,並且不會出現快速排序可能出現的最壞情況。這兩種排序都是不穩定的。
 若要求排序穩定,則可選用歸併排序。但本章介紹的從單個記錄起進行兩兩歸併的 排序演算法並不值得提倡,通常可以將它和直接插入排序結合在一起使用。先利用直接插入排序求得較長的有序子檔案,然後再兩兩歸併之。因為直接插入排序是穩定 的,所以改進後的歸併排序仍是穩定的。

優先佇列通常用堆排序來實現