計數排序
計數排序的原理
設被排序的數組爲A,排序後存儲到B,C爲臨時數組。所謂計數,首先是通過一個數組C[i]計算大小等於i的元素個數,此過程只需要一次循環遍歷就可以;在此基礎上,計算小於或者等於i的元素個數,也是一重循環就完成。下一步是關鍵:逆序循環,從length[A]到1,將A[i]放到B中第C[A[i]]個位置上。原理是:C[A[i]]表示小於等於a[i]的元素個數,正好是A[i]排序後應該在的位置。而且從length[A]到1逆序循環,可以保證相同元素間的相對順序不變,這也是計數排序穩定性的體現。在數組A有附件屬性的時候,穩定性是非常重要的。
計數排序的前提及適用範圍
A中的元素不能大於k,而且元素要作爲數組的下標,所以元素應該爲非負整數。而且如果A中有很大的元素,不能夠分配足夠大的空間。所以計數排序有很大侷限性,其主要適用於元素個數多,但是普遍不太大而且總小於k的情況,這種情況下使用計數排序可以獲得很高的效率。由於用來計數的數組C的長度取決於待排序數組中數據的範圍(等於待排序數組的最大值與最小值的差加上1),這使得計數排序對於數據範圍很大的數組,需要大量時間和內存。例如:計數排序是用來排序0到100之間的數字的最好的算法,但是它不適合按字母順序排序人名。但是,計數排序可以用在基數排序中的算法來排序數據範圍很大的數組。
當輸入的元素是 n 個 0 到 k 之間的整數時,它的運行時間是 Θ(n + k)。計數排序不是比較排序,排序的速度快於任何比較排序算法。
計數排序算法的步驟:
1.找出待排序的數組中最大和最小的元素
2.統計數組中每個值爲i的元素出現的次數,存入數組C的第i項
3.對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加)
4.反向填充目標數組:將每個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1
實現代碼:
void CountSort(int *a, int size) { int min = a[0], max = a[0]; int i = 0; for ( i = 0; i < size; i++) { if (min > a[i]) { min = a[i];//找出數組中最小的數 } if (max < a[i]) { max = a[i];//找出數組中的最大數 } } int range = max - min + 1; int *count = new int[range]; //初始化數組 //memset(count, 0, sizeof(int)*range); for (i = 0; i < range; i++) { count[i] = 0; } //把數組a中數變成數組count中的0,1,2.... for (i = 0; i < size; i++) { //把數組count中對應位置製成數字,代表這個位置有幾個相同的數 //列如製成1,代表這個位置有一個數 //列如製成2,代表這個位置有兩個相同的數 count[a[i] - min]++; } int j = 0; //把count中的數還原回數組a中,它就排好序了 for (i = 0; i < range; i++) { //重複了n次,就拿回去n次 while (count[i]>0) { a[j++] = i + min; count[i]--; } } delete[] count; }
基數排序
算法思想:
待排序數組[62,14,59,88,16]簡單點五個數字
分配10個桶,桶編號爲0-9,以個位數數字爲桶編號依次入桶,變成下邊這樣
| 0 | 0 | 62 | 0 | 14 | 0 | 16 | 0 | 88 | 59 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |桶編號
將桶裏的數字順序取出來,
輸出結果:[62,14,16,88,59]
再次入桶,不過這次以十位數的數字爲準,進入相應的桶,變成下邊這樣:
由於前邊做了個位數的排序,所以當十位數相等時,個位數字是由小到大的順序入桶的,就是說,入完桶還是有序
| 0 | 14,16 | 0 | 0 | 0 | 59 | 62 | 0 | 88 | 0 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |桶編號
因爲沒有大過100的數字,沒有百位數,所以到這排序完畢,順序取出即可
最後輸出結果:[14,16,59,62,88]
實現代碼:
//獲取最大位數 static int GetMaxRadix(int *a, int size) { int radix = 10; int count = 1; for (int i = 0; i < size; i++) { //注意這裏必須是">=",假如你的最大數是100,如果 //沒有“=”的話,你獲取最大位就是兩位 while (a[i]>=radix) { radix *= 10; count++; } } return count; } static void _RadixSort(int* a, int size, int divisor, int* tmp) { int count[10] = { 0 }; int start[10] = { 0 }; //如果你處理的是個位,count代表就是數據個位在 //count對應位置出現的個數。十位,百位類似。 for (int i = 0; i < size; i++) { int num1 = a[i] / divisor; count[num1 % 10]++; } //個位,十位,百位等出現的起始位置 for (int j = 1; j < 10; j++) { start[j] = start[j - 1] + count[j - 1]; } //根據start,將a中的數據放在tmp中,已排好序 for (int k = 0; k < size; k++) { int num2 = a[k] / divisor; tmp[start[num2 % 10]++] = a[k]; } //把排好序的數據放回a中 for (int n = 0; n < size; n++) { a[n] = tmp[n]; } } void RadixSort(int *arr, int size) { assert(arr); int *tmp = new int[size]; int n = GetMaxRadix(arr, size); for (int i = 1; i <= n; i++) { int divisor = 1; int k = i; while (--k) { divisor *= 10; } _RadixSort(arr, size, divisor, tmp); } delete[] tmp; }