本系列的文章列表和相關說明,請查看【一塊兒學習排序算法】0 序言
也能夠直接到github上查看完整的文章和源碼!javascript
排序算法(Sorting algorithms)是什麼? Wikipedia 如是說:html
In computer science, a sorting algorithm is an algorithm that puts elements of a list in a certain order.java
也就是說,排序算法,就是某種算法,將列表中的元素按照某種規則排序。常見的如數字大小排序、字典序排序等。本系列例子約定爲從小到大的數字排序,其餘的相似,關鍵在於思路。git
一、內部排序和外部排序github
按照數組規模的大小,排序能夠分爲內部排序
和外部排序
。
內部排序(internal sorting): 所有數組均可放在內存中排序。
外部排序(external sorting): 數組太大,不能所有放在內存中,部分數據在硬盤中。算法
本系列約定爲內部排序,關於海量數據的排序,後續補充。數組
二、穩定性
排序法的穩定性(stability): 取決於值相等的兩個元素,排序以後是否保持原來的順序。數據結構
三、比較排序和非比較排序
比較排序(comparison sort):
比較排序中,每一步經過比較元素大小來決定元素的位置。其複雜度由比較次數
和交換次數
來決定。比較排序比較好實現,可是時間複雜度沒法突破O(nlogn)
。證實過程,能夠參考這篇文章。函數
非比較排序(non-comparison sort):
非比較排序,如桶排序,不經過比較,後續將會講解。這類算法能夠突破O(nlogn)
。post
排序算法有不少種,每一種都各自有本身的優勢缺點和不一樣的應用場景,沒有一種排序是絕對完美的。如何評價一個算法的優劣呢,咱們經過算法複雜度
來衡量。
算法複雜度(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記法
。
對於單調函數 f(n) 和 g(n), n爲正整數,若是存在常數c > 0, n0 > 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是簡寫,通常忽略底數。
二次項時間(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(n2)。平均仍是O(n2)。算法複雜度的優劣,能夠參考此圖:
本章節主要介紹了一下排序算法的類型,以及若是經過大O記法來評價一個算法。對於如何計算算法的時間複雜度,不少人都感受很頭疼。我給的建議是,按照上面的步驟多練習,多去主動算程序的時間複雜度。這樣慢慢本身就會掌握技巧,而且提醒本身保證本身程序的執行效率。共勉!
[1] About the #sorting-algorithms series
[2] 凱耐基梅隆大學數據結構與算法-排序算法
[3] CMU algorithm complexity
[4] Big O cheat sheet
[5] You need to understand Big O notation, now