桶排序、計數排序、基數排序這三種排序算法由於時間複雜度是O(n)線性的,所以把這類排序算法叫作線性排序。他們是非基於比較的排序算法。算法
這種排序算法對排序的數據要求很苛刻,主要掌握他們的適用場景。數組
以上,能夠看出實際使用桶排序時,須要數據在各個桶間分佈均勻,若是某些桶內數據很是多,某些又不多,則每一個桶內數據排序的時間複雜度就不是很是小的常量了。在極端狀況下,全部數據都進到一個桶內,O(nlogn);bash
外部排序:數據存儲在外部磁盤中,數據量大,內存有限,沒法將數據所有加載到內存。ui
此時使用桶排序,就能夠將數據按照桶的順序,一桶桶加載到內存進行排序。spa
能夠當作一種特殊的桶排序,它先找到數據的最大值k(也就是數據的範圍),而後將數據劃分到k個桶內。則每一個桶內的數據值都是相等的,省了桶內排序的時間。code
可是具體計算,不是直接將數據存入桶內,而是將數據的個數放入桶內,再用計數的方式算出位置。cdn
// 計數排序,a 是數組,n 是數組大小。假設數組中存儲的都是非負整數。
public void countingSort(int[] a, int n) {
if (n <= 1) return;
// 查找數組中數據的範圍
int max = a[0];
for (int i = 1; i < n; ++i) {
if (max < a[i]) {
max = a[i];
}
}
int[] c = new int[max + 1]; // 申請一個計數數組 c,下標大小 [0,max]
for (int i = 0; i <= max; ++i) {
c[i] = 0;
}
// 計算每一個元素的個數,放入 c 中
for (int i = 0; i < n; ++i) {
c[a[i]]++;
}
// 依次累加
for (int i = 1; i <= max; ++i) {
c[i] = c[i-1] + c[i];
}
// 臨時數組 r,存儲排序以後的結果
int[] r = new int[n];
// 計算排序的關鍵步驟,有點難理解
for (int i = n - 1; i >= 0; --i) {
int index = c[a[i]]-1;
r[index] = a[i];
c[a[i]]--;
}
// 將結果拷貝給 a 數組
for (int i = 0; i < n; ++i) {
a[i] = r[i];
}
}
複製代碼
綜上,計數排序只能用在數據範圍不大的場景中,若是數據最大範圍K比數據量n大不少,就會有不少空桶,不適用。且計數排序只能給非負整數排序,若是數據是其餘類型,能夠在不改變相對大小的狀況下,轉化爲非負整數。blog
相似對手機號碼這樣的數據排序,由於數據範圍大因此桶排序和計數都不行。排序
基數排序:按照每一位數據進行穩定排序。內存
每一位排序可使用桶排序或計數,則時間複雜度O(n)。排序數據有k爲=O(kn),若是k較小,則接近O(n);
若是不是等長的數據能夠補齊,如用‘0’這樣不影響原有大小順序的數據。
綜上:基數排序要求數據能夠分割出獨立的‘位’來比較,且位之間有遞進關係(如:個十百位)。同時,每一位的數據範圍不要太大,要可使用線性排序算法來排序。不然時間複雜度就達不到n了。