全到细到你不敢相信的排序算法性能对比


前言

本文是排序算法系列的第六篇,是一个总结篇

但它不是最后一篇~

还有一个堆排序,因为涉及到二叉树的概念,不太好写,暂时放在后边

本文将在多个方面(稳定性、时间性能、空间性能)对所有排序算法进行对比

参与对比的有下列共十种排序算法:

冒泡排序、选择排序、插入排序、二分插入排序、希尔排序、快速排序、归并排序、基数排序、计数排序、堆排序

稳定性对比

本文主要对比性能,稳定性只展示个结论,不做集具体说明,这里再解释一次稳定性的概念:

稳定性就是,比如下面这组数字

31,44(a),44(b),86,18,28

有两个相同的44,给两个44打上标记,a号44和b号44

排好后两种可能:18,28,31,44(b),44(a),86 或 18,28,31,44(a),44(b),86

两个44谁前谁后都不影响排序的结果是正确的

但两个44和排序前相比,如果被逆序了(b号44跑到a号44前面),这种就称为不稳定

稳定的排序就是,除了完成排序规则必须进行的顺序改变外,不改变原有的顺序

也就是两个数字相等的时候原先在前面的还在前面,原先在后面的还在后面,才叫稳定排序

结论在下方时间性能对比的表格中

时间性能对比

理论时间复杂度:

r代表关键字基数,d代表长度,n代表关键字个数

排序算法 平均 最好 最坏 稳定性
冒泡排序 O(n^2) O(n) O(n^2) 稳定
选择排序 O(n^2) O(n) O(n^2) 不稳定
插入排序 O(n^2) O(n) O(n^2) 稳定
二分插入排序 O(n^2) O(n) O(n^2) 稳定
希尔排序 O(n^1.3) O(n) O(n^2) 不稳定
快速排序 O(nlogn) O(nlogn) O(n^2) 不稳定
归并排序 O(nlogn) O(nlogn) O(nlogn) 稳定
基数排序 O(d(r+n)) O(d(r+n)) O(d(r+n)) 稳定
计数排序 O(n) O(n) O(n) 额外说明(1)
堆排序 O(nlogn) O(nlogn) O(nlogn) 不稳定

计数排序稳定性说明:计数排序优势在于只对整数排序时,记录数量即可,所以一般不考虑稳定性,如果考虑稳定性,会多占用非常大量的空间,得不偿失

实际测试排序用时:

1.测试环境

同一台电脑、相同运行环境、JDK1.8、idea 2021.1.1x64

2.测试数据

区间:[-5*n,5*n] 中随机生成的 n 个整数,例如8w数据将在 [-40w,40w] 区间生成随机的8w数据

    /**
     * 生成num个随机的正负整数
     * (从-5*num到5*num的范围,生成num个整数)
     *
     * @param num 生成的数量
     * @return num个随机整数的int数组
     */
    private static int[] getIntsAll(int num) {
        int[] arr = new int[num];
        for (int i = 0; i < num; i++) {
            arr[i] = (int) (Math.random()*(Math.random()>0.5?1:-1) * num*5);
        }
        return arr;
    }

为了严谨的计算时间,会在如下代码中间执行排序算法

保证排序正确的前提下,得到执行时间

所有排序算法的代码,都在之前的文章中有,我也会整理本次测试使用到的所有代码,贴在文章的末尾,读者可自行拿来复测

long startTime = System.currentTimeMillis();

//这里是排序算法的执行
//BubbleSort.bubbleSort(arr);//冒泡
//SelectSort.selectSort(arr);//选择
//InsertSort.insertSort3(arr);//插入(while循环)
//InsertSort.insertSort4(arr);//插入(for循环)
//InsertSort.binarySort(arr);//二分插入排序
//ShellSort.shellSort2(arr);//希尔排序
//QuickSort.quickSort3(arr);//快速排序(填坑法)
//QuickSort.quickSort3(arr);//快速排序(交换法)
//MergeSort.mergeSort(arr);//归并排序
//RadixSort.radixSortPlus(arr);//基数排序(支持负整数版)
//CountSort.countSort(arr);//计数排序(支持负整数版)
//HeapSort.heapSort(arr);//堆排序

System.out.println("总执行时长(毫秒):"+(System.currentTimeMillis()-startTime));

//这里调用了一个判断数组是否有序的方法,防止算法因为写错进行了错误的排序,从而导致的数据没有参考价值
System.out.println(ifSort(arr));
//方法如下:
/**
 * 判断一个数组是否是从小到大有序的
 * @param arr 待判断数组
 * @return 是否有序
 */
static boolean ifSort(int[] arr){
    for (int i = 0; i < arr.length-1; i++) {
        if (arr[i+1]<arr[i]){
            return false;
        }
    }
    return true;
}

3.测试结果

测试结果都分别执行七次,去掉一个最快一个最慢,余下五个取平均值,单位都是毫秒

排序算法 8w 800w 8000w 800w次10个数据排序
冒泡排序 8224 太慢 太慢 1177.8
选择排序 2353.6 太慢 太慢 567.2
插入(while循环) 603.6 太慢 太慢 832.8
插入(for循环) 625.6 太慢 太慢 793.6
二分插入排序 1209.8 太慢 太慢 1246.6
希尔排序 14.6 2019.2 29726 960.2
快速排序(填坑法) 10.8 852.2 9296.4 1438.4
快速排序(交换法) 11.2 918.4 9891.8 1350.4
归并排序 12.6 1140.6 12151.4 3623.4
基数排序(支持负整数版) 20.8 550.4 内存溢出 6841
计数排序(支持负整数版) 6.4 386 内存溢出 2984.2
堆排序 6.4 377.8 3914.4 696.2
JDK排序Arrays.sort() 16.6 734.6 7527.4 770.8

注:详细测试数据放在文末

4.性能排名

排名 8w 800w 8000w 800w次10个数据排序
1 堆排序 堆排序 377.8 计数排序 选择排序
2 计数排序 计数排序 堆排序 堆排序
3 快速排序(填坑法) 基数排序 基数排序 JDK排序
4 快速排序(交换法) JDK排序 JDK排序 插入(for循环)
5 归并排序 快速排序(填坑法) 快速排序(填坑法) 插入(while循环)
6 希尔排序 快速排序(交换法) 快速排序(交换法 希尔排序
7 JDK排序 归并排序 归并排序 冒泡排序
8 基数排序 希尔排序 希尔排序 二分插入排序
9 插入(while循环) 快速排序(交换法)
10 插入(for循环) 快速排序(填坑法)
11 二分插入排序 计数排序
12 选择排序 归并排序
13 冒泡排序 基数排序

8000w数据基数和计数排序都内存溢出了,为了比较,降低了数量级

用2000w数据进行了测试,结论是:计数>堆>基数>JDK,类推到8000w得出的数据

5.测试结论分析

首先测试数据具有一定的局限性,属于是均匀散列的随机整数组,未考虑特殊情况比如小范围内的大量数据,或大范围内的少量数据等。

1.堆排序效率几乎任何数据量情况都是第一第二,稳居榜首

2.计数排序数据量越大越好,但其实这里对计数排序测试不够细致,计数排序如果数据范围很大,数据量很小会很不友好,而数据范围很小数据量很大时是非常快的。

3.快速排序数据量较大时,填坑法实现性能优于交换法,数据量特效小时交换法优于填坑法

4.同作为非比较排序,基数排序性能比计数排序差,而两者都随着数据量增大性能排名有所提升

5.实在是有点搞不懂,为什么二分插入排序性能实测弱与插入排序...貌似这并不是插入排序的一个好的优化

6.JDK排序性能并没有表现很好,弱于所有非比较排序,也弱于堆排序,数据量较小时表现也不佳(值得关注的是,JDK源码中,数据量小于47时,完全使用的是插入排序,却比我自己实现的插入排序性能要略好一些,后续有空时应该稍作研究,看看差在哪里)

7.选择、冒泡、插入,完全不适合大数据量的排序,效率非常低

8.实践得出的结果表明,在对10个数据排序的性能表现中,最优秀的是选择排序,但JDK排序源码中却使用了插入排序作为数据量低于47时的排序方式,或许是经过测试数据<47时,插入排序综合性能更好?

9.对于for实现的插入和while实现的插入,比较发现,执行次数较多时,while优于for,次数较少时好像是for更快?

暂时就这么多,读者朋友有什么新结论可在评论区讨论~

空间性能对比

这里不做过多的测试和赘述,直接上结论

排序算法 空间复杂度
冒泡排序 O(1)
选择排序 O(1)
插入排序 O(1)
二分插入排序 O(1)
希尔排序 O(1)
快速排序 O(logn)~O(n)
归并排序 O(n)
基数排序 O(k(n+1)——k为桶的数量
计数排序 O(M)——M为数据区间范围
堆排序 O(1)

附录

附录一:测试使用的排序算法代码

1.冒泡排序

	public static void bubbleSort(int[] arr){
        //用来表示某一趟发生的排序次数
        int times = 0;

        //外层控制总趟数,所以i=1表示从第一趟开始,当等于length就不执行了也就是一共执行length-1趟
        for (int i = 1; i < arr.length; i++) {
            //内层,进行从第1个数到第length-i个数,依次与后一个数字比较,逆序就交换
            // 这里让j=0,因为第一个数下标为0
            for (int j = 0; j < arr.length-i; j++) {
                if (arr[j]>arr[j+1]){
                    //进入这里说明发生了一次交换
                    times++;
                    int t = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = t;
                }
            }
            //一趟排序结束,打印一下这一趟后的数组,以及发生了几次交换
//            System.out.println("第"+i+"趟后的数组:");
//            System.out.println(Arrays.toString(arr));
//            System.out.println("这一趟发生了"+times+"次交换");
            //如果发生过交换,就重置次数为0进入下一次循环
            if (times>0){
                times = 0;
            }
            //否则说明这一趟没有进行任何交换,直接跳出循环即可
            else {
//                System.out.println("本轮没有进行交换,排序已完成");
                break;
            }
        }
//        System.out.println(Arrays.toString(arr));
    }

2.选择排序

    public static void selectSort(int[] arr){
        //记录某一趟的最小值的索引
        int minIndex;
        //比较变量——最终保存某一趟的最小值
        int min;
        for (int i = 0; i < arr.length-1; i++) {
            //每一趟排序开始前,把这一趟要扫描的第一个数放到比较变量上,假定他就是最小的
            minIndex = i;
            min = arr[i];
            for (int j = i+1; j < arr.length; j++) {
                if (arr[j]<min) {
                    min = arr[j];
                    minIndex = j;
                }
            }
            //一趟排序扫描完毕后,是第几趟就把这一趟找到的最小值和第几个交换
            if (minIndex!=i){
                //如果当前i索引位置本身就是最小值了,就没有必要执行交换操作
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }

3.while插入

    public static void insertSort3(int[] arr){
        for (int i = 1; i < arr.length; i++) {
            //待插入的数
            int insertVal = arr[i];
            int insertIndex = i-1;
            /*
             * 1.insertIndex>=0保证插入位置索引不越界
             * 2.insertVal < arr[insertIndex]表示待插入数据还没有找到应该插入的位置
             * 满足这两个条件,需要将arr[insertIndex]后移,insertIndex--
             */
            while (insertIndex>=0 && insertVal < arr[insertIndex]){
                arr[insertIndex+1] = arr[insertIndex];
                insertIndex--;
            }
            //退出while循环后,说明当前insertIndex+1就是待插入数据的位置
            arr[insertIndex+1] = insertVal;
        }
    }

4.for插入

    public static void insertSort4(int[] arr){
        //从第二个数开始遍历
        for (int i = 1; i < arr.length; i++) {
            //待插入的数存进临时变量
            int temp = arr[i];
            //记录待插入数应该插入的位置(初始化为要被插入的数据本身的位置)
            int index = i;
            //内层遍历待插入数前面的所有数去找到他的位置
            for (int j = i-1; j >= 0 && arr[j]>temp; j--) {
                /*
                 * 依次向下标i之前的数据,挨个比较
                 * 如果当前位置数据比自己大就将其后移,记录下当前这个位置的索引
                 * 后续有两种情况会退出这个循环:
                 * 1.当前要遍历的数字比自己小时就不会再进入循环了,而那个上一次循环中记录下来的索引位置就是要插入的位置
                 * 2.如果一直到索引0的位置还没有找到比自己小的,下一次也会退出循环,上一次循环中记录的索引位置必然为0,也是要插入的位置
                 */
                arr[j+1]=arr[j];
                index = j;
            }
            //综上只要是退出了循环,index就是指向数据要插入的位置,直接插入就好
            arr[index] = temp;
        }
    }

5.二分插入

    public static void binarySort(int[] source) {
        int temp;
        int low, mid, high;
        for (int i = 1; i < source.length; i++) {
            temp = source[i];
            // 在待插入排序的序号之前进行折半插入
            low = 0;
            high = i - 1;
            while (low <= high) {
                mid = (low + high) / 2;
                if (temp < source[mid])
                    high = mid - 1;
                else
                    // low=high的时候也就是找到了要插入的位置,
                    // 此时进入循环中,将low加1,则就是要插入的位置了
                    low = mid + 1;
            }
            //找到了要插入的位置,从该位置一直到插入数据的位置之间数据向后移动
            for (int j = i; j >= low + 1; j--){
                source[j] = source[j - 1];
            }
            // low已经代表了要插入的位置了
            source[low] = temp;
        }
    }

6.希尔排序

    public static void shellSort2(int[] arr) {
        int temp, j;
        for (int path = arr.length / 2; path > 0; path /= 2) {
            for (int i = path; i < arr.length; i++) {
                j = i;
                temp = arr[j];
                while (j - path >= 0 && temp < arr[j - path]) {
                    arr[j] = arr[j - path];
                    j -= path;
                }
                arr[j] = temp;
            }
        }
    }

7.快速排序(填坑法)

    public static void quickSort2(int[] arr){
        quickSort2(arr,0, arr.length-1);
    }
    private static void quickSort2(int[] arr, int start, int end){
        //1.找一个数字作为基准p,让这个数左边都<p,右边都>=p
        int index = partition2(arr, start, end);
        //2.完成之后,比基准数字小都都在左边,大的都在右边,再重复对左右两边的部分进行上述操作
        //3.考虑递归边界,判断如果左右边总数据量小于两个(0个或1个)就不再递归了
        if (index-1-start>=1){
            quickSort2(arr,start,index-1);
        }
        if (end-index-1>=1){
            quickSort2(arr,index+1,end);
        }
    }
    private static int partition2(int[] arr, int start, int end) {
        //1.指定第一个数为基准数字,将基准数字挖出来存在这里,原来的位置当做一个坑用于排序
        int p = arr[start];
        //2.定义两个指针,一个在最左边,一个最右边
        int left = start;
        int right = end;
        //3.只要左指针还在右指针的左边,就循环用右边找一个小于p的填左边坑,右边作为新坑,再从左边找一个>=p的填右边的坑
        while (left<right){
            //进入循环坑一定在左边,所以先从右向左
            //先从右到左寻找比基准数字小的,找到就让右指针指向这个数字
            while (left<right && arr[right] >= p){
                right--;
            }
            //退出从右到左的寻找循环表示找到了,或者left=right了
            if (left==right){
                //说明排好了
                break;
            }
            //到这里没有退出循环说明找到了,用它填左边的坑,再将左指针后移,此时右指针位置变成新坑
            arr[left] = arr[right];
            left++;
            //再从左到右寻找比基准数字大的,找到就让左指针指向这个数字
            while (left<right && arr[left] < p){
                left++;
            }
            //退出从左到右的寻找循环表示找到了或者left==right了
            if (left==right){
                //说明排好了
                break;
            }
            //到这里没有退出循环说明找到了,用它填右边的坑,再将右指针前移,此时左指针位置变成新坑
            arr[right] = arr[left];
            right--;
        }
        //直到退出大循环,此时的left一定等于right,就是基准数字应该在的位置,将基准数字安插在这里
        arr[left] = p;
        //返回基准数字所在位置索引
        return left;
    }

8.快速排序(交换法)

    public static void quickSort3(int[] arr){
        quickSort3(arr,0, arr.length-1);
    }
    private static void quickSort3(int[] arr, int start, int end){
        //1.找一个数字作为基准p,让这个数左边都<p,右边都>=p
        int index = partition3(arr, start, end);
        //2.完成之后,比基准数字小都都在左边,大的都在右边,再重复对左右两边的部分进行上述操作
        //3.考虑递归边界,判断如果左右边总数据量小于两个(0个或1个)就不再递归了
        if (index-1-start>=1){
            quickSort3(arr,start,index-1);
        }
        if (end-index-1>=1){
            quickSort3(arr,index+1,end);
        }
    }
    private static int partition3(int[] arr, int start, int end) {
        //1.指定第一个数为基准数字
        int p = arr[start];
        //2.定义两个指针,一个在基准数的下一个,一个在最右边,用于左到右和右到左的扫描
        int left = start+1;
        int right = end;
        //3.保持一个循环,再循环中判断排好,直接用返回结束循环
        while (true){
            //这里先左到右或先右到左都可以
            //先从左指针开始向右指针扫描(不用扫右指针及右指针之后的),直到发现一个大于基准数字的就指向它
            while (left < right && arr[left] <= p){
                left++;
            }
            //再从右指针开始向左扫描(需要扫左指针及左指针之后的),直到发现一个小于等于基准数字的就指向他
            while (arr[right] > p){
                right--;
            }
            //这里有两种情况需要区别对待,如果right>left,说明两个指针交错前两边都找到了,两个数据进行交换
            if (right>left){
                int temp = arr[left];
                arr[left] = arr[right];
                arr[right] = temp;
            }
            //否则说明两边交错后才找到,此时交换基准数字和right就排好了,返回基准数字索引right
            else {
                int temp = arr[start];
                arr[start] = arr[right];
                arr[right] = temp;
                return right;
            }
        }
    }

9.归并排序

    public static void mergeSort(int[] arr){
        MergeSort.mergeSort(arr,0,arr.length-1,new int[arr.length]);
    }
    private static void mergeSort(int[] arr, int start, int end, int[] temp){
        //如果数组段中的数据大于等于2个,就分割成左右两边
        if (start < end){
            //中间数索引(左边的终点)
            int mid = (start + end) / 2;
            mergeSort(arr, start, mid, temp);
            mergeSort(arr, mid + 1, end, temp);
            //无论是否需要再次分割,分割完毕或不分割,都执行一次合并操作
            merge(arr,start,mid,end,temp);
        }
    }
    private static void merge(int[] arr, int start, int mid, int end, int[] temp) {
        //左起点为start,左终点为mid,右起点为mid+1,右终点为end
        //左指针
        int lPointer = start;
        //右指针
        int rPointer = mid + 1;
        //临时数组指针
        int t = 0;
        //当左右指针都没越界比较左右两个指针指向数据的大小,谁小就把谁放在临时数组指针位置,并将指针后移一个
        while (lPointer<= mid && rPointer<= end){
            if (arr[lPointer]<arr[rPointer]) {
                temp[t++] = arr[lPointer++];
            }else {
                temp[t++] = arr[rPointer++];
            }
        }
        //当某个指针没越界,剩余的都放入临时数组
        while (lPointer<= mid){
            temp[t++] = arr[lPointer++];
        }
        while (rPointer<= end){
            temp[t++] = arr[rPointer++];
        }
        //将排好序的temp放回arr的start到end
        int index = start;
        t = 0;
        while (index<=end){
            arr[index++] = temp[t++];
        }
    }

10.基数排序

    public static void radixSortPlus(int[] arr){
        //1.判断最小的数字是否是负数,如果不是就按正整数处理,如果是,就获取这个值
        int minNum = arr[0];

        //在一个循环中,获取到最小的数字
        for (int i : arr) {
            if (i<minNum){
                minNum = i;
            }
        }
        //如果最小的数字是负数,遍历数组,给所有数字减去这个负数(相当于加上这个数的绝对值)
        if (minNum<0){
            for (int i = 0; i < arr.length; i++) {
                arr[i]-=minNum;
            }
        }

        //减去最小的负数后,整个数组被转换为非负整数了,进行非负整数的基数排序
        radixSort(arr);

        //排序结束后,如果最小的数字是负数,再遍历数组,给每个数据加上这个负数(相当于减去这个数的绝对值)
        if (minNum<0){
            for (int i = 0; i < arr.length; i++) {
                arr[i]+=minNum;
            }
        }
    }

11.计数排序

    public static void countSort(int[] arr){
        //设置默认最大最小值
        int min=arr[0];
        int max=arr[0];
        //找出最大,最小值
        for (int value : arr) {
            if (max < value) {
                max = value;
            }
            if (min > value) {
                min = value;
            }

        }
        //创建计数数组
        int[] countArr=new int[max-min+1];
        //遍历待排序数组,确定每个数字的数量存入技术数组
        for (int value : arr) {
            countArr[value - min]++;
        }
        //遍历计数数组,把数据按顺序放回arr
        int begin=0;
        for (int i = 0; i < countArr.length; i++) {
            if (countArr[i]!=0){
                //有几个当前数字,就放几个
                for (int j = 0; j <countArr[i] ; j++) {
                    arr[begin]=min+i;
                    begin++;
                }
            }
        }
    }

12.堆排序

    public static void heapSort(int[] arr){
        //从右到左,从下到上,遍历每个非叶子节点,都执行一次转换为大顶堆的方法
        // 此时整个数组都是一个大顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--){
            toLargeTopHeap(arr,i,arr.length);
        }
        //此时的arr已经全部转化为了大顶堆的形状,arr[0]就是数组中的最大值
        for (int j = arr.length-1; j>0 ; j--){
            //将最大值和最后一个数据交换位置
            int temp = arr[0];
            arr[0] = arr[j];
            arr[j] = temp;
            //交换后,对大顶堆的破坏只有两个数据,
            // 最后一个arr[j]等于排好序了,下次不参与,所以不用管,
            // 相当于只影响了第一个arr[0],
            // arr[1]到arr[j-1]都还是大顶堆
            // 就只需要对arr[0]做前j个数据转化大顶堆的操作
            toLargeTopHeap(arr,0,j);
            //然后继续循环交换再转换,直到交换后只剩一个数字就排好序了
        }
    }
    private static void toLargeTopHeap(int[] arr, int i, int length){
        //临时存储当前非叶子节点的值,等找到他的位置再去存放
        int temp = arr[i];
        //相当于此时的i位置被空出来了

        //进入一个循环,这里理解起来比较复杂:
        // 1.首先让k指向i的左子节点k=2*i+1
        for (int k = i * 2 + 1; k < length;) {
            //2.再将k指向i的左右子节点中更大的一个
            if (k+1 < length && arr[k] < arr[k+1]){
                //进入这里说明右子节点更大
                k++;
            }
            //3.判断需要找到位置的temp和左右子节点中更大的一个arr[k]的大小关系
            if (arr[k] > temp){
                //如果arr[k]更大,说明原先arr[i]的位置应该存放arr[k]
                arr[i] = arr[k];
                //i位置被arr[k]填上了,相当于k位置空出来了,让i指针移动过来
                i = k;
                //然后k会指向新i的左子节点,然后进入下一次循环,继续寻找temp的位置
                // (其实这个可以写在for的第三个表达式处k = k * 2 + 1)
                k = i * 2 + 1;
            }
            //因为下方必然是大顶堆的形状,那么如果temp更大,说明i这里就是temp的位置
            else {
                arr[i] = temp;
                //temp的位置找到了,退出循环
                break;
            }
        }
    }

附录二:详细测试数据

冒泡:

8w: 8189、8237、8203、8210、8220、8312、8250

800w次10个数据排序: 1182、1170、1162、1218、1170、1171、1196

选择:

​ **8w: **2338、2339、2312、2310、2469、2288、2498

800w次10个数据排序: 565、569、576、564、564、569、569

while插入:

8w: 613、587、602、622、593、596、614

800w次10个数据排序: 834、833、835、829、833、831、833

for插入:

8w: 647、627、628、613、604、636、624

800w次10个数据排序: 792、793、794、790、795、794、796

二分插入:

8w: 1245、1208、1203、1185、1208、1176、1287(不理解为什么比插入慢。。。)

800w次10个数据排序: 1232、1245、1266、1260、1253、1233、1242

希尔:

8w: 12、17、12、12、11、11、11

800w: 1855、1986、2146、2086、2067、1964、1993

8000w: 29540、30355、28921、29402、32338、30412、26619

800w次10个数据排序: 947、945、951、932、1001、957、1009

填坑法快速:

8w: 10、11、10、12、11、7、12

800w: 860、900、842、838、860、861、800

8000w: 9864、9013、9205、9052、9348、10004、8940

800w次10个数据排序: 1409、1469、1409、1415、1523、1454、1445

交换法快速:

8w: 12、11、13、11、9、10、12

800w: 911、936、887、861、921、972、937

8000w: 9951、9987、10106、9986、9727、9808、9709

800w次10个数据排序: 1342、1364、1335、1345、1345、1357、1363

归并:

8w: 11、12、13、12、12、42、14

800w: 1134、1167、1125、1160、1096、1117、1180

8000w: 12160、12185、12163、12124、11896、12125、12196

800w次10个数据排序: 3611、3599、3631、3637、3639、3548、3644

基数:

8w: 26、14、25、26、14、25、13

800w: 555、531、569、556、537、557、547

2000w: 1359、1211、1245、1220、1230、1228、1263(1237.2)

8000w: 内存溢出...这个需要开辟的空间和计数排序差不多

800w次10个数据排序: 6863、6810、6825、6832、6827、6932、6858

计数:

8w: 6、7、7、6、6、6、8

800w: 388、379、387、379、398、391、385

2000w: 954、942、983、926、995、933、996(961.4)

8000w: 内存溢出...需要开辟880000000个int[]空间,一个int是4个字节,所需内存为:8.8亿✖4/(1024✖1024✖1024)=3.27GB的连续空间内存,当前环境溢出了,没法测

800w次10个数据排序: 3044、2952、3032、2990、2987、2960、2911

堆排序:

8w: 7、6、6、7、5、7、6

800w: 453、394、397、433、348、305、317

2000w: 1004、1320、823、821、1073、1157、1297(1070.8)

8000w: 3385、4329、4258、4228、3403、3403、4280

800w次10个数据排序: 689、699、671、693、701、703、699

JDK排序Arrays.sort():

8w: 19、12、21、9、21、19、12

800w: 754、713、797、749、717、731、722

8000w: 7608、7484、7798、7424、8199、7282、7548

800w次10个数据排序: 773、769、770、771、770、771、772

博客标签


end
  • 作者:coderZ(联系作者)
  • 发表时间:2022-07-28 14:29:49
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转载栈主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者公众号二维码(公众号二维码见右边,欢迎关注)


  • 评论

    暂无评论,抢个沙发?



    输入评论、昵称、邮箱,选择头像后发布留言

    选择您的头像