【一起學習排序演算法】1 演算法特性及大O記法
排序演算法(Sorting algorithms)是什麼?Wikipedia 如是說:
In computer science, a sorting algorithm is an algorithm that puts elements of a list in a certain order.
也就是說,排序演算法,就是某種演算法,將列表中的元素按照某種規則排序。常見的如數字大小排序、字典序排序等。本系列例子約定為從小到大的數字排序,其他的類似,關鍵在於思路。
演算法特性
1、內部排序和外部排序
按照陣列規模的大小,排序可以分為 內部排序
和 外部排序
。
內部排序(internal sorting):全部陣列都可放在記憶體中排序。
外部排序(external sorting):陣列太大,不能全部放在記憶體中,部分資料在硬碟中。
本系列約定為內部排序,關於海量資料的排序,後續補充。
2、穩定性
排序法的穩定性(stability): 取決於值相等的兩個元素,排序之後是否保持原來的順序。
3、比較排序和非比較排序
比較排序(comparison sort):
比較排序中,每一步通過比較元素大小來決定元素的位置。其複雜度由 比較次數
和 交換次數
來決定。比較排序比較好實現,但是時間複雜度無法突破 O(nlogn)
。證明過程,可以參考這篇文章。
非比較排序(non-comparison sort):
非比較排序,如桶排序,不通過比較,後續將會講解。這類演算法可以突破 O(nlogn)
。
排序演算法有很多種,每一種都各自有自己的優點缺點和不同的應用場景,沒有一種排序是絕對完美的。如何評價一個演算法的優劣呢,我們通過 演算法複雜度
來衡量。
演算法複雜度
演算法複雜度(complexity),可以從 時間複雜度
和 空間複雜度
兩個維度來考慮。
空間複雜度,是指演算法所需要的額外的儲存單元。目前的硬體條件,這一塊通常可以不考慮了。演算法優化,更多是來優化演算法的時間。
下面將介紹如何來估算時間複雜度。下面的介紹的方法,目前只夠勉強說服我自己。如果覺得不想了解這個理論,可以直接記住下面的結論。如果覺得講得不是那麼容易懂,可以參考別的資料仔細研究。
時間複雜度
如果一個列表的大小為n,則演算法耗費的時間T(n)。但是由於機器、CPU等的不同,同一個演算法執行的時間可能都不一樣。所以通常不是按耗費的時間來計算,而是用某個演算法實現的 指令執行的次數
,來衡量時間複雜度。如下面這個程式:
for( i = 0; i < n; i++)// i = 0; 執行1次 // i < n; 執行n+1次 // i++執行n次 sum = sum + i;//執行n次 // 總次數f(n) = 1 + n+1 + n +n = 3n+2 複製程式碼
通過上面計數運算元的方法,顯得很麻煩。所以通常是通過一個函式來估算,確保它是演算法運算元f(n)的上界。這種方法就是 大O記法
。
大O記法
對於單調函式 f(n) 和 g(n), n為正整數,如果存在常數c > 0, n 0 > 0,且
則我們稱
如下圖所示。

簡單來說,就是當n→∞時,f(n)的增長率不大於g(n),也就是說g(n)時f(n)的上界。 在這裡,f(n)就是演算法的指令運算元,而g(n)就是我們估算的複雜度上界。 它還有兩個特性。
所以,上面程式的時間複雜度是:
-
常數時間 O(1)
常數時間(constant time)
,演算法的執行時間和列表大小無關。 -
線性時間 O(n)
線性時間(linear time)
, 演算法執行時間和列表大小成正比。 -
對數時間 O(logn)
對數時間(logarithmic time)
, 稍微顯得難理解一點。不過如果你瞭解對數,其實也很簡單。例如二分查詢,每一次查詢都會去掉一半的元素,但最後一次元素個數就是1。假設陣列大小為n, 要經過x輪查詢,則
logn是簡寫,一般忽略底數。
-
二次項時間 O(n 2 )
二次項時間(quadratic time)
, 通常是兩層迴圈的演算法。
簡易估算方法
對於一個演算法的時間複雜度,根據以上理論,大體按下面的步驟來估算複雜度。 以這個程式為例:
sum = 0; for( i = 0; i < n; i++) for( j = i; j < n; j++) sum++; 複製程式碼
1. 忽略簡單語句
對於簡單複雜語句,它執行次數是一個常數,複雜度為O(1)。如果還存在迴圈,O(1)對結果不影響。
2. 關注迴圈語句
對於迴圈語句,要認真分析其迴圈執行的次數。例子中,外層迴圈要執行 n 次,內層迴圈要
所以總次數T(n)為
3. 忽略常數項,保留高次項
對於一個多項式,當n→∞時,完全由最高項次決定。所以
對於有的程式,複雜度還是很不好計算。所以要多練習,寫一個程式之後,自己主動去算一下它的複雜度,慢慢就熟練了。
演算法評價
對於排序演算法,一個演算法的執行效能,和輸入的資料有很大的關係。對於某些特定的資料,某些演算法的效率很高,但通常演算法的效能又很低。所以通常存在:
- 最優時間複雜度:某些資料,執行的次數最少
- 最差時間複雜度:某些資料,執行的次數最多
- 平均時間複雜度:平均需要執行的次數
通常還是以平均時間複雜度,來衡量演算法。例如氣泡排序,當陣列元素有序時,最優時間複雜度為O(n)。當逆序是,為O(n 2 )。平均還是O(n 2 )。演算法複雜度的優劣,可以參考此圖:
