java sorted排序_【算法】排序算法之计数排序
前幾回,我們已經(jīng)對(duì)冒泡排序、直接插入排序、希爾排序、選擇排序、快速排序、歸并排序、堆排序做了說(shuō)明分析。本回,將對(duì)計(jì)數(shù)排序進(jìn)行相關(guān)說(shuō)明分析。
一、排序算法系列目錄說(shuō)明
- 冒泡排序(Bubble Sort)
- 插入排序(Insertion Sort)
- 希爾排序(Shell Sort)
- 選擇排序(Selection Sort)
- 快速排序(Quick Sort)
- 歸并排序(Merge Sort)
- 堆排序(Heap Sort)
- 計(jì)數(shù)排序(Counting Sort)
- 桶排序(Bucket Sort)
- 基數(shù)排序(Radix Sort)
二、計(jì)數(shù)排序(Counting Sort)
計(jì)數(shù)排序(Counting sort)是一種穩(wěn)定的線性時(shí)間排序算法。
1. 基本思想
計(jì)數(shù)排序使用一個(gè)額外的數(shù)組C,其中第i個(gè)元素是待排序數(shù)組A中值等于i的元素的個(gè)數(shù)。
計(jì)數(shù)排序的核心在于將輸入的數(shù)據(jù)值轉(zhuǎn)化為鍵存儲(chǔ)在額外開(kāi)辟的數(shù)組空間中。作為一種線性時(shí)間復(fù)雜度的排序,計(jì)數(shù)排序要求輸入的數(shù)據(jù)必須是有確定范圍的整數(shù)。
用來(lái)計(jì)數(shù)的數(shù)組C的長(zhǎng)度取決于待排序數(shù)組中數(shù)據(jù)的范圍(等于待排序數(shù)組的最大值與最小值的差加上1),然后進(jìn)行分配、收集處理:
① 分配。掃描一遍原始數(shù)組,以當(dāng)前值-minValue作為下標(biāo),將該下標(biāo)的計(jì)數(shù)器增1。② 收集。掃描一遍計(jì)數(shù)器數(shù)組,按順序把值收集起來(lái)。
2. 實(shí)現(xiàn)邏輯
① 找出待排序的數(shù)組中最大和最小的元素② 統(tǒng)計(jì)數(shù)組中每個(gè)值為i的元素出現(xiàn)的次數(shù),存入數(shù)組C的第i項(xiàng)
③ 對(duì)所有的計(jì)數(shù)累加(從C中的第一個(gè)元素開(kāi)始,每一項(xiàng)和前一項(xiàng)相加)
④ 反向填充目標(biāo)數(shù)組:將每個(gè)元素i放在新數(shù)組的第C(i)項(xiàng),每放一個(gè)元素就將C(i)減去1
3. 動(dòng)圖演示
計(jì)數(shù)排序演示舉個(gè)例子,假設(shè)有無(wú)序數(shù)列nums=[2, 1, 3, 1, 5], 首先掃描一遍獲取最小值和最大值,maxValue=5, minValue=1,于是開(kāi)一個(gè)長(zhǎng)度為5的計(jì)數(shù)器數(shù)組counter
(1) 分配統(tǒng)計(jì)每個(gè)元素出現(xiàn)的頻率,得到counter=[2, 1, 1, 0, 1],例如counter[0]表示值0+minValue=1出現(xiàn)了2次。(2) 收集
counter[0]=2表示1出現(xiàn)了兩次,那就向原始數(shù)組寫(xiě)入兩個(gè)1,counter[1]=1表示2出現(xiàn)了1次,那就向原始數(shù)組寫(xiě)入一個(gè)2,依次類推,最終原始數(shù)組變?yōu)閇1,1,2,3,5],排序好了。
4. 復(fù)雜度分析
平均時(shí)間復(fù)雜度:O(n + k)最佳時(shí)間復(fù)雜度:O(n + k)
最差時(shí)間復(fù)雜度:O(n + k)
空間復(fù)雜度:O(n + k)
當(dāng)輸入的元素是n 個(gè)0到k之間的整數(shù)時(shí),它的運(yùn)行時(shí)間是 O(n + k)。。在實(shí)際工作中,當(dāng)k=O(n)時(shí),我們一般會(huì)采用計(jì)數(shù)排序,這時(shí)的運(yùn)行時(shí)間為O(n)。
計(jì)數(shù)排序需要兩個(gè)額外的數(shù)組用來(lái)對(duì)元素進(jìn)行計(jì)數(shù)和保存排序的輸出結(jié)果,所以空間復(fù)雜度為O(k+n)。
計(jì)數(shù)排序的一個(gè)重要性質(zhì)是它是穩(wěn)定的:具有相同值的元素在輸出數(shù)組中的相對(duì)次序與它們?cè)谳斎霐?shù)組中的相對(duì)次序是相同的。也就是說(shuō),對(duì)兩個(gè)相同的數(shù)來(lái)說(shuō),在輸入數(shù)組中先出現(xiàn)的數(shù),在輸出數(shù)組中也位于前面。
計(jì)數(shù)排序的穩(wěn)定性很重要的一個(gè)原因是:計(jì)數(shù)排序經(jīng)常會(huì)被用于基數(shù)排序算法的一個(gè)子過(guò)程。我們將在后面文章中介紹,為了使基數(shù)排序能夠正確運(yùn)行,計(jì)數(shù)排序必須是穩(wěn)定的。
5. 代碼實(shí)現(xiàn)
C版本:
// 計(jì)數(shù)排序(C) #include <stdio.h> #include <stdlib.h> #include <time.h> void print_arr(int *arr, int n) {int i;printf("%d", arr[0]);for (i = 1; i < n; i++)printf(" %d", arr[i]);printf("n"); } void counting_sort(int *ini_arr, int *sorted_arr, int n) {int *count_arr = (int *) malloc(sizeof(int) * 100);int i, j, k;for (k = 0; k < 100; k++)count_arr[k] = 0;for (i = 0; i < n; i++)count_arr[ini_arr[i]]++;for (k = 1; k < 100; k++)count_arr[k] += count_arr[k - 1];for (j = n; j > 0; j--)sorted_arr[--count_arr[ini_arr[j - 1]]] = ini_arr[j - 1];free(count_arr); } int main(int argc, char **argv) {int n = 10;int i;int *arr = (int *) malloc(sizeof(int) * n);int *sorted_arr = (int *) malloc(sizeof(int) * n);srand(time(0));for (i = 0; i < n; i++)arr[i] = rand() % 100;printf("ini_array: ");print_arr(arr, n);counting_sort(arr, sorted_arr, n);printf("sorted_array: ");print_arr(sorted_arr, n);free(arr);free(sorted_arr);return 0; }Java版本:
// 計(jì)數(shù)排序(Java) public class CountingSort {public static void main(String[] argv) {int[] A = CountingSort.countingSort(new int[]{16, 4, 10, 14, 7, 9, 3, 2, 8, 1});Utils.print(A);}public static int[] countingSort(int[] A) {int[] B = new int[A.length];// 假設(shè)A中的數(shù)據(jù)a'有,0<=a' && a' < k并且k=100int k = 100;countingSort(A, B, k);return B;}private static void countingSort(int[] A, int[] B, int k) {int[] C = new int[k];// 計(jì)數(shù)for (int j = 0; j < A.length; j++) {int a = A[j];C[a] += 1;}Utils.print(C);// 求計(jì)數(shù)和for (int i = 1; i < k; i++) {C[i] = C[i] + C[i - 1];}Utils.print(C);// 整理for (int j = A.length - 1; j >= 0; j--) {int a = A[j];B[C[a] - 1] = a;C[a] -= 1;}} }6. 優(yōu)化改進(jìn)
場(chǎng)景分析:舉個(gè)極端的例子:如果排序的數(shù)組有200W個(gè)元素,但是這200W個(gè)數(shù)的值都在1000000-1000100,也就說(shuō)有100個(gè)數(shù),總共重復(fù)了200W次,現(xiàn)在要排序,怎么辦?
這種情況排序,計(jì)數(shù)排序應(yīng)該是首選。但是這時(shí)候n的值為200W,如果按原來(lái)的算法,k的值10001000,但是此時(shí)c中真正用到的地方只有100個(gè),這樣對(duì)空間造成了極大的浪費(fèi)。
改進(jìn)思路:針對(duì)c數(shù)組的大小,優(yōu)化計(jì)數(shù)排序
改進(jìn)代碼:
// 計(jì)數(shù)排序優(yōu)化(Java) // 針對(duì)c數(shù)組的大小,優(yōu)化計(jì)數(shù)排序 public class CountSort{public static void main(String []args){//排序的數(shù)組int a[] = {100, 93, 97, 92, 96, 99, 92, 89, 93, 97, 90, 94, 92, 95};int b[] = countSort(a);for(int i : b){System.out.print(i + " ");}System.out.println();}public static int[] countSort(int []a){int b[] = new int[a.length];int max = a[0], min = a[0];for(int i : a){if(i > max){max = i;}if(i < min){min = i;}}//這里k的大小是要排序的數(shù)組中,元素大小的極值差+1int k = max - min + 1;int c[] = new int[k];for(int i = 0; i < a.length; ++i){c[a[i]-min] += 1;//優(yōu)化過(guò)的地方,減小了數(shù)組c的大小}for(int i = 1; i < c.length; ++i){c[i] = c[i] + c[i-1];}for(int i = a.length-1; i >= 0; --i){b[--c[a[i]-min]] = a[i];//按存取的方式取出c的元素}return b;} }三、總結(jié)
計(jì)數(shù)算法只能使用在已知序列中的元素在0-k之間,且要求排序的復(fù)雜度在線性效率上。 ? 計(jì)數(shù)排序和基數(shù)排序很類似,都是非比較型排序算法。但是,它們的核心思想是不同的,基數(shù)排序主要是按照進(jìn)制位對(duì)整數(shù)進(jìn)行依次排序,而計(jì)數(shù)排序主要側(cè)重于對(duì)有限范圍內(nèi)對(duì)象的統(tǒng)計(jì)。基數(shù)排序可以采用計(jì)數(shù)排序來(lái)實(shí)現(xiàn)。
下一篇預(yù)告:桶排序(Bucket Sort)。欲知詳情,且聽(tīng)下回分解。
PS: 更多資源,歡迎關(guān)注微信公眾號(hào):developer1024
總結(jié)
以上是生活随笔為你收集整理的java sorted排序_【算法】排序算法之计数排序的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 查找这个接口的调用_事务处理不当,线上接
- 下一篇: 拟合一条曲线_数据预测与曲线拟合