Java常见的几种排序

Java常见的几种排序

一、什么是排序

    对于什么是排序,相信大家已经有了一个初步的印象,就是把一串记录按照一定的规律进行排列,可以让使用者查看看起来更方便,能帮助我们更好的处理事务。
    比如一串数字,可以按照大小,选择升序或是降序进行排列;文字可以按照笔画多少或声母进行排列;英文字母就可以根据ABCD的顺序进行排列;或者根据自己的需求,进行排列。

二、排序分类

    排序算法可以分为内部排序和外部排序。内部排序是数据记录在内存中进行排序。外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:冒泡排序、选择排序、插入排序、希尔排序、快速排序、堆排序等。按照排序的方法,可以分为简单排序、高级排序等。

简单排序:

    冒泡排序、选择排序、插入排序

高级排序:

    快速排序、归并排序、希尔排序

相关算法知识:

    堆排序、计数排序、桶排序、基数排序

根据接口排序:

    Collection.sort、TreeMap

三、基本原理

一、冒泡排序

1、原理
    它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
    这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
2、编码思路
    1、从第一个数据开始,与第二个数据相比较,如果第二个数据小于第一个数据,则交换两个数据的位置。
    2、指针由第一个数据移向第二个数据,第二个数据与第三个数据相比较,如果第三个数据小于第二个数据,则交换两个数据的位置。
    3、依此类推,完成第一轮排序。第一轮排序结束后,最大的元素被移到了最右面。
    4、依照上面的过程进行第二轮排序,将第二大的排在倒数第二的位置。
    5、重复上述过程,没排完一轮,比较次数就减少一次。

在这里插入图片描述
如图所示,在升序排序中,每一轮比较完后,最大的数字就会到最后面相应的位置,需要两层循环,第一层循环i表示排序的轮数,第二层循环j表示比较的次数。

3、案例
//首先声明一个数组
        int[] array = {5,3,2,4,8};

        //第一层循环为外循环,遍历次数
        for (int i = 0 ; i < array.length;i++){
            //第二层循环为内循环,比较大小,进行排序
            //内层循环一次,获取一个最大值
            for (int j = i+1 ; j < array.length-1 ; j ++ ){
                //比较相邻两个数的大小,如果前一个值比后一个值大,则交换,降序排序相反
                if ( array[i] > array[j] ){
                    int temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }
            }
        }
    最终输出的结果是:2,3,4,5,8。

二、选择排序

1、原理
    选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法。
2、编码思路

在这里插入图片描述

    1、首先在未排序序列中找到第一位元素,拟定它最小(大)值,记录下索引。
    2、再把它跟剩下的元素依次进行比较,遇到比之前更小(大)的值,就将索引更换为当前值得索引。
    3、每一轮循环完后,如果索引与之前的索引不同,就说明有比之前拟定的最小值小(大)的值,就将它们两个交换。
    4、重复前面三个步骤,不过每一轮的拟定最小(大)的值往后移一位,直到循环结束。
    5、注意:每次循环完后,将i前面的数据看成一个排好的队列,i后面的看成一个无序队列。每次只需要找无序的最小值,做替换就可以了。
3、案例
//首先声明一个数组
        int[] array = {5,3,2,4,8};

        //第一层循环,从第一位数值开始比
        for (int i = 0 ; i < array.length ; i++){
            //拟定第一位数值为最小值,记录下该数值与索引
            int min = array[i];
            int minIndex = i;
            //第二层循环,依次与后面的值进行比较
            for (int j = i+1;j < array.length-1 ; j ++ ){
                //如果遇到比拟定值小的,就把拟定值换成目前的最小值,记录下索引
                if (min>array[j]){
                    min = array[j];
                    minIndex = j ;
                }
            }

            //循环完成后就将索引为i的值与索引为minIndex的值进行交换
            //此时索引为minIndex的值才是最小值
            int temp = array[i];
            array[i] = array[minIndex];
            array[minIndex] = temp ;
        }

三、插入排序

1、原理
    插入排序的代码实现虽然没有冒泡排序和选择排序那么简单粗暴,但它的原理应该是最容易理解的了,因为只要打过扑克牌的人都应该能够秒懂。插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
    插入排序和冒泡排序一样,也有一种优化算法,叫做拆半插入。
    它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动
2、编码思路
    1、将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
    2、从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

在这里插入图片描述

3、案例
//首先声明一个数组
        int[] array = {5,3,2,4,8,1};

        //第一层循环,选择需要拿出来比较的数值,从第二位开始。
        //因为第一位已经默认是排好了序,需要从后面的数去和它比,所以索引从1开始。
        for (int i = 1 ; i < array.length-1 ; i++){

            //第二层循环,依次向前取一位数进行比较,直到第一位
            for (int j = i+1 ; j > 0 ; j -- ){

                //如果前一个数比它大,那么就将两个数交换
                if (array[j-1] > array[j]){

                    int temp = array[j];
                    array[j] = array[j-1];
                    array[j-1] = temp;
                }
            }
        }

四、快速排序

1、原理
    快速排序(Quicksort)是对冒泡排序算法的一种改进。
    设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
2、编码思路
    1、首先设定一个分界值,通过该分界值将数组分成左右两部分。
    2、将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
    3、然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
    4、重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

在这里插入图片描述

3、案例
public static int[] qsort(int arr[],int start,int end) {        
    int pivot = arr[start];        
    int i = start;        
    int j = end;        
    while (i<j) {            
        while ((i<j)&&(arr[j]>pivot)) {                
            j--;            
        }            
        while ((i<j)&&(arr[i]<pivot)) {                
            i++;            
        }            
        if ((arr[i]==arr[j])&&(i<j)) {                
            i++;            
        } else {                
            int temp = arr[i];                
            arr[i] = arr[j];                
            arr[j] = temp;            
        }        
    }        
    if (i-1>start) arr=qsort(arr,start,i-1);        
    if (j+1<end) arr=qsort(arr,j+1,end);        
    return (arr);    
}    
 
public static void main(String[] args) {        
    int arr[] = new int[]{3,3,3,7,9,122344,4656,34,34,4656,5,6,7,8,9,343,57765,23,12321};        
    int len = arr.length-1;        
    arr=qsort(arr,0,len);        
    for (int i:arr) {            
        System.out.print(i+"\t");        
    }    
}
 
/*//方式二*/
更高效点的代码:(TextendsComparable和SortUtil都是自己封装的类,里面重写和实现了compareTo和swap方法)
public <TextendsComparable<?superT>>
T[] quickSort(T[] targetArr,int start,int end)
{
inti=start+1,j=end;
T key=targetArr[start];
SortUtil<T> sUtil=new SortUtil<T>();
 
if(start==end)return(targetArr);
 
 
/*从i++和j--两个方向搜索不满足条件的值并交换
*
*条件为:i++方向小于key,j--方向大于key
*/
while(true)
{
while(targetArr[j].compareTo(key)>0)j--;
while(targetArr[i].compareTo(key)<0&&i<j)i++;
if(i>=j)break;
sUtil.swap(targetArr,i,j);
if(targetArr[i]==key)
{
j--;
}else{
i++;
}
}
 
/*关键数据放到‘中间’*/
sUtil.swap(targetArr,start,j);
 
if(start<i-1)
{
this.quickSort(targetArr,start,i-1);
}
if(j+1<end)
{
this.quickSort(targetArr,j+1,end);
}
 
returntargetArr;
}
 
 
/*//方式三:减少交换次数,提高效率/*/
private<TextendsComparable<?superT>>
voidquickSort(T[]targetArr,intstart,intend)
{
inti=start,j=end;
Tkey=targetArr[start];
 
while(i<j)
{
/*按j--方向遍历目标数组,直到比key小的值为止*/
while(j>i&&targetArr[j].compareTo(key)>=0)
{
j--;
}
if(i<j)
{
/*targetArr[i]已经保存在key中,可将后面的数填入*/
targetArr[i]=targetArr[j];
i++;
}
/*按i++方向遍历目标数组,直到比key大的值为止*/
while(i<j&&targetArr[i].compareTo(key)<=0)
/*此处一定要小于等于零,假设数组之内有一亿个1,0交替出现的话,而key的值又恰巧是1的话,那么这个小于等于的作用就会使下面的if语句少执行一亿次。*/
{
i++;
}
if(i<j)
{
/*targetArr[j]已保存在targetArr[i]中,可将前面的值填入*/
targetArr[j]=targetArr[i];
j--;
}
}
/*此时i==j*/
targetArr[i]=key;//应加判断
 
/*递归调用,把key前面的完成排序*/
this.quickSort(targetArr,start,i-1);
 
 
/*递归调用,把key后面的完成排序*/
this.quickSort(targetArr,j+1,end);
//两个递归应加判断
}

五、归并排序

1、原理
    归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
    作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法:
    1、自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);
    2、自下而上的迭代;
2、编码思路
   1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
   2、设定两个指针,最初位置分别为两个已经排序序列的起始位置
   3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
   4、重复步骤3直到某一指针超出序列尾
   5、将另一序列剩下的所有元素直接复制到合并序列尾

在这里插入图片描述

3、案例
public class MergeSort implements IArraySort {

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        if (arr.length < 2) {
            return arr;
        }
        int middle = (int) Math.floor(arr.length / 2);

        int[] left = Arrays.copyOfRange(arr, 0, middle);
        int[] right = Arrays.copyOfRange(arr, middle, arr.length);

        return merge(sort(left), sort(right));
    }

    protected int[] merge(int[] left, int[] right) {
        int[] result = new int[left.length + right.length];
        int i = 0;
        while (left.length > 0 && right.length > 0) {
            if (left[0] <= right[0]) {
                result[i++] = left[0];
                left = Arrays.copyOfRange(left, 1, left.length);
            } else {
                result[i++] = right[0];
                right = Arrays.copyOfRange(right, 1, right.length);
            }
        }

        while (left.length > 0) {
            result[i++] = left[0];
            left = Arrays.copyOfRange(left, 1, left.length);
        }

        while (right.length > 0) {
            result[i++] = right[0];
            right = Arrays.copyOfRange(right, 1, right.length);
        }

        return result;
    }

}

六、希尔排序

1、原理
    希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。
    希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
2、编码思路
    1、选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
    2、按增量序列个数 k,对序列进行 k 趟排序;
    3、每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

在这里插入图片描述

3、案例
int length = arr.length;
    int temp;
    for (int step = length / 2; step >= 1; step /= 2) {
        for (int i = step; i < length; i++) {
            temp = arr[i];
            int j = i - step;
            while (j >= 0 && arr[j] > temp) {
                arr[j + step] = arr[j];
                j -= step;
            }
            arr[j + step] = temp;
        }
    }

七、堆排序

1、原理
    堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
    可以说是一种利用堆的概念来排序的选择排序。分为两种方法:
    大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
    小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;
2、编码思路
    1、创建一个堆 H[0……n-1];
    2、把堆首(最大值)和堆尾互换;
    3、把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
    4、重复步骤 2,直到堆的尺寸为 1。

在这里插入图片描述

3、案例
public class HeapSort implements IArraySort {

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        int len = arr.length;

        buildMaxHeap(arr, len);

        for (int i = len - 1; i > 0; i--) {
            swap(arr, 0, i);
            len--;
            heapify(arr, 0, len);
        }
        return arr;
    }

    private void buildMaxHeap(int[] arr, int len) {
        for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
            heapify(arr, i, len);
        }
    }

    private void heapify(int[] arr, int i, int len) {
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int largest = i;

        if (left < len && arr[left] > arr[largest]) {
            largest = left;
        }

        if (right < len && arr[right] > arr[largest]) {
            largest = right;
        }

        if (largest != i) {
            swap(arr, i, largest);
            heapify(arr, largest, len);
        }
    }

    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

}

八、计数排序

1、原理
    计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 [1]  当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)。
2、编码思路
    1、找出待排序的数组中最大和最小的元素
    2、统计数组中每个值为i的元素出现的次数,存入数组C的第i项
    3、对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
    4、反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

在这里插入图片描述

3、案例
public int[] sort(int[] sourceArray) throws Exception {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        int maxValue = getMaxValue(arr);

        return countingSort(arr, maxValue);
    }

    private int[] countingSort(int[] arr, int maxValue) {
        int bucketLen = maxValue + 1;
        int[] bucket = new int[bucketLen];

        for (int value : arr) {
            bucket[value]++;
        }

        int sortedIndex = 0;
        for (int j = 0; j < bucketLen; j++) {
            while (bucket[j] > 0) {
                arr[sortedIndex++] = j;
                bucket[j]--;
            }
        }
        return arr;
    }

    private int getMaxValue(int[] arr) {
        int maxValue = arr[0];
        for (int value : arr) {
            if (maxValue < value) {
                maxValue = value;
            }
        }
        return maxValue;
    }

九、桶排序

1、原理
    桶排序 (Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。桶排序是鸽巢排序的一种归纳结果。当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))。但桶排序并不是 比较排序,他不受到 O(n log n) 下限的影响。
2、编码思路
    桶排序算法要求,数据的长度必须完全一样,程序过程要产生长度相同的数据,使用下面的方法:Data=rand()/10000+10000上面提到的,每次下一次的扫描顺序是按照上次扫描的结果来的,所以设计上提供相同的两个桶数据结构。前一个保存每一次扫描的结果供下次调用,另外一个临时拷贝前一次扫描的结果提供给前一个调用。
    元素分布在桶中:

在这里插入图片描述
然后,元素在每个桶中排序:
在这里插入图片描述

3、案例
     private static final InsertSort insertSort = new InsertSort ( ) ;

    @Override
    public int [ ] sort ( int [ ] sourceArray ) throws Exception {
        // 对 arr 进行复制,不改变参数内容
        int [ ] arr = Arrays . copyOf ( sourceArray,sourceArray。长度);

        返回桶排序( arr, 5 ) ;
    }

    私人 INT [ ]桶排序(INT [ ] ARR,INT bucketSize ) 抛出 异常 {
        如果 ( ARR。长度 == 0 ) {
            返回ARR ;
        }

        int minValue = arr [ 0 ] ;
        int maxValue = arr [ 0 ] ;
        对于 (整数值: arr ) {
            if ( value < minValue ) {
                minValue = value ;
            } else if ( value > maxValue ) {
                maxValue = value ;
            }
        }

        int bucketCount = ( int ) 数学。floor ( ( maxValue - minValue ) / bucketSize ) + 1 ;
        int [ ] [ ]桶= new int [ bucketCount ] [ 0 ] ;

        //利用映射函数将数据分配到各个桶中
        对 (INT我= 0 ;我<。ARR长度;我++ ) {
            INT指数= (INT ) 数学。地板( ( arr [ i ] - minValue ) /桶大小) ;
            桶[索引] = arrAppend (桶[索引] , arr [ i ] ) ;
        }

        int arrIndex = 0 ;
        对 (INT [ ]桶:桶) {
            如果 (。铲斗长度 <= 0 ) {
                继续;
            }
            // 对每个桶进行排序,这里使用了插入
            桶=插入排序。排序(桶);
            for ( int value : bucket ) {
                arr [ arrIndex ++ ] = value ;
            }
        }

        返回ARR ;
    }

    /**
     * 自动扩容,并保存数据
     *
     * @param arr
     * @param value
     */
    private int [ ] arrAppend ( int [ ] arr, int value ) {
        arr = 数组。copyOf (。ARR,ARR长度 + 1 );
        arr [ arr。长度 - 1 ] =值;
        返回arr ;
    }

十、基数排序

1、原理
    基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
    基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
2、编码思路
    第一步
    以LSD为例,假设原来有一串数值如下所示:
    73, 22, 93, 43, 55, 14, 28, 65, 39, 81
    首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
    0
    1 81
    2 22
    3 73 93 43
    4 14
    5 55 65
    6
    7
    8 28
    9 39
    第二步
    接下来将这些桶子中的数值重新串接起来,成为以下的数列:
    81, 22, 73, 93, 43, 14, 55, 65, 28, 39
    接着再进行一次分配,这次是根据十位数来分配:
    0
    1 14
    2 22 28
    3 394 43
    5 55
    6 65
    7 73
    8 81
    9 93
    第三步
    接下来将这些桶子中的数值重新串接起来,成为以下的数列:
    14, 22, 28, 39, 43, 55, 65, 73, 81, 93
    这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。
    LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好。MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。在进行完最低位数的分配后再合并回单一的数组中。
3、案例
/**
 * 基数排序
 * 考虑负数的情况还可以参考: https://code.i-harness.com/zh-CN/q/e98fa9
 */
public class RadixSort implements IArraySort {

    @Override
    public int[] sort(int[] sourceArray) throws Exception {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);

        int maxDigit = getMaxDigit(arr);
        return radixSort(arr, maxDigit);
    }

    /**
     * 获取最高位数
     */
    private int getMaxDigit(int[] arr) {
        int maxValue = getMaxValue(arr);
        return getNumLenght(maxValue);
    }

    private int getMaxValue(int[] arr) {
        int maxValue = arr[0];
        for (int value : arr) {
            if (maxValue < value) {
                maxValue = value;
            }
        }
        return maxValue;
    }

    protected int getNumLenght(long num) {
        if (num == 0) {
            return 1;
        }
        int lenght = 0;
        for (long temp = num; temp != 0; temp /= 10) {
            lenght++;
        }
        return lenght;
    }

    private int[] radixSort(int[] arr, int maxDigit) {
        int mod = 10;
        int dev = 1;

        for (int i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
            // 考虑负数的情况,这里扩展一倍队列数,其中 [0-9]对应负数,[10-19]对应正数 (bucket + 10)
            int[][] counter = new int[mod * 2][0];

            for (int j = 0; j < arr.length; j++) {
                int bucket = ((arr[j] % mod) / dev) + mod;
                counter[bucket] = arrayAppend(counter[bucket], arr[j]);
            }

            int pos = 0;
            for (int[] bucket : counter) {
                for (int value : bucket) {
                    arr[pos++] = value;
                }
            }
        }

        return arr;
    }

    /**
     * 自动扩容,并保存数据
     *
     * @param arr
     * @param value
     */
    private int[] arrayAppend(int[] arr, int value) {
        arr = Arrays.copyOf(arr, arr.length + 1);
        arr[arr.length - 1] = value;
        return arr;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值