常见排序的JAVA实现和性能测试

写了五种常见的排序算法实现
算法描述
1.插入排序
这里写图片描述

从第一个元素开始,该元素可以认为已经被排序
取出下一个元素,在已经排序的元素序列中从后向前扫描
如果该元素(已排序)大于新元素,将该元素移到下一位置
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
将新元素插入到该位置后
重复步骤2~5

在这个基础上有衍生出提高效率的二分插入排序

2.冒泡排序
这里写图片描述

比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

最简单的排序,名字很形象,排序元素会呈现上浮或者下沉的特点,

3.希尔排序
这里写图片描述
先取一个正整数 d1(d1 < n),把全部记录分成 d1 个组,所有距离为 d1 的倍数的记录看成一组,然后在各组内进行插入排序
然后取 d2(d2 < d1)
重复上述分组和排序操作;直到取 di = 1(i >= 1) 位置,即所有记录成为一个组,最后对这个组进行插入排序。一般选 d1 约为 n/2,d2 为 d1 /2, d3 为 d2/2 ,…, di = 1。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:
  插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
  但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
4.堆排序
这里写图片描述

堆的特点
通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:
父节点i的左子节点在位置(2*i+1);
父节点i的右子节点在位置(2*i+2);
子节点i的父节点在位置floor((i-1)/2);
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:
最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
创建最大堆(Build_Max_Heap):将堆所有数据重新排序
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

5.归并排序
这里写图片描述

迭代法
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针到达序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
递归法
原理如下(假设序列共有n个元素):
将序列每相邻两个数字进行归并操作,形成 {\displaystyle floor(n/2)} floor(n/2)个序列,排序后每个序列包含两个元素
将上述序列再次归并,形成 {\displaystyle floor(n/4)} floor(n/4)个序列,每个序列包含四个元素
重复步骤2,直到所有元素排序完毕

5.快速排序
这里写图片描述

从数列中挑出一个元素,称为”基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

性能测试的结果

少量数据
insertionSort正序数列:585031ns
insertionSort倒序数列:585886ns
insertionSort随机数列:392587ns
insertionSort周期数列:108196ns

bubbleSort正序数列:559372ns
bubbleSort倒序数列:1126442ns
bubbleSort随机数列:637633ns
bubbleSort周期数列:129152ns

shellSort正序数列:41055ns
shellSort倒序数列:79543ns
shellSort随机数列:124876ns
shellSort周期数列:41910ns

heapSort正序数列:112901ns
heapSort倒序数列:82109ns
heapSort随机数列:41055ns
heapSort周期数列:37206ns

mergeSort正序数列:116749ns
mergeSort倒序数列:127441ns
mergeSort随机数列:129152ns
mergeSort周期数列:71419ns

quickSort正序数列:45331ns
quickSort倒序数列:67569ns
quickSort随机数列:73984ns
quickSort周期数列:52601ns

大量数据
insertionSort正序数列:1527354269ns
insertionSort倒序数列:1545648468ns
insertionSort随机数列:1578804082ns
insertionSort周期数列:433450353ns

bubbleSort正序数列:543612833ns
bubbleSort倒序数列:1136154882ns
bubbleSort随机数列:3859670355ns
bubbleSort周期数列:541080691ns

shellSort正序数列:3903629ns
shellSort倒序数列:5970055ns
shellSort随机数列:6844181ns
shellSort周期数列:744119ns

heapSort正序数列:6524295ns
heapSort倒序数列:4414676ns
heapSort随机数列:8049738ns
heapSort周期数列:5399564ns

mergeSort正序数列:8257150ns
mergeSort倒序数列:3990015ns
mergeSort随机数列:7866274ns
mergeSort周期数列:4171341ns

quickSort正序数列:2582604ns
quickSort倒序数列:4825224ns
quickSort随机数列:7015242ns
quickSort周期数列:4657156ns


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Sort{
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        Sort s1 = new Sort();
        System.out.println("少量数据");
        int arraySize = 200;
        s1.test("insertionSort",arraySize);
        s1.test("bubbleSort",arraySize);
        s1.test("shellSort",arraySize);
        s1.test("heapSort",arraySize);
        s1.test("mergeSort",arraySize);
        s1.test("quickSort",arraySize);
        System.out.println("大量数据");
        arraySize = 50000;
        s1.test("insertionSort",arraySize);
        s1.test("bubbleSort",arraySize);
        s1.test("shellSort",arraySize);
        s1.test("heapSort",arraySize);
        s1.test("mergeSort",arraySize);
        s1.test("quickSort",arraySize);
    }

    //测试的脚手架
    public void test(String methodName,int size) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        Method m1 = this.getClass().getMethod(methodName,int[].class);
        int[] test1 = new int[size];
        int[] test2 = new int[size];
        int[] test3 = new int[size];
        int[] test4 = new int[size];
        Sort sorttest = new Sort();
        //正序
        for(int i = 0; i < size;i++){
            test1[i] = i;
        }
        //倒序
        for(int i = 0; i < size;i++){
            test2[i] = size - i;
        }
        //乱序,随机,基本无重复元素
        for(int i = 0;i < size;i++){
            test3[i] = (int) (Math.random() * size);
        }
        //大量重复元素
        for(int i = 0; i < size;i++){
            test4[i] = i + 50 % 50;
        }
        long startTime=System.nanoTime(); 
        long endTime=System.nanoTime();  
        m1.invoke(sorttest, test1);
        startTime = System.nanoTime();
        m1.invoke(sorttest, test1);
        endTime = System.nanoTime();
        System.out.println(methodName +"正序数列:" +(endTime - startTime) + "ns");

        startTime=System.nanoTime();    
        m1.invoke(sorttest, test2);
        endTime=System.nanoTime(); 
        System.out.println(methodName +"倒序数列:" +(endTime - startTime) + "ns");

        startTime=System.nanoTime();     
        m1.invoke(sorttest, test3);
        endTime=System.nanoTime();  
        System.out.println(methodName +"随机数列:" +(endTime - startTime) + "ns");

        startTime=System.nanoTime();     
        m1.invoke(sorttest, test4);
        endTime=System.nanoTime();  
        System.out.println(methodName +"周期数列:" +(endTime - startTime) + "ns");

        System.out.println();
    }

    //插入排序,稳定排序
    //插入排序由N-1趟排序组成,时间最好O(n),最坏O(n2),平均O(n2)
    //空间O(1)
    public void insertionSort(int[] nums){
        int j,p;
        int tmp;
        for(p = 1;p < nums.length; p++){
            tmp = nums[p];
            for(j = p;j > 0;j--){
                if(nums[j-1] > tmp)
                nums[j] = nums[j-1];
            }
            nums[j] = tmp;
        }
    }
    //冒泡排序,稳定排序
    //时间最好O(n),最坏O(n2),平均O(n2)
    //空间O(1)
    public void bubbleSort(int[] nums){
        int j,p;
        int tmp;
        //沉水,大数被移动到尾段
        for(p = 0;p < nums.length - 1;p++){
            for(j = 0;j < nums.length - 1 - p;j++){
                if(nums[j] > nums[j+1]){
                    tmp = nums[j];
                    nums[j] = nums[j+1];
                    nums[j+1] = tmp;
                }
            }
        }
//      //气泡,小数浮动到首段
//      for(p = 0; p < nums.length - 1;p++){
//          for(j = nums.length - 1;j > p;j--){
//              if(nums[j-1] > nums[j]){
//                  tmp = nums[j];
//                  nums[j] = nums[j-1];
//                  nums[j-1] = tmp;
//              }
//          }
//      }
    }
    //希尔排序(缩小增量排序),不稳定排序
    //属于插入排序,时间最好O(n),最坏O(n2),平均O(n1.3)
    //空间O(1)
    public void shellSort(int[] nums){
        int gap = 1;            //增量
        int i,j;
        int len = nums.length;
        int tmp;
        //初始增量,shell排序的效率与增量设定有很大关系
        while(gap < len / 3)
            gap = gap * 3 + 1;
        for(;gap > 0;gap /= 3){
            for (i = gap; i < len; i++) {
                tmp = nums[i];
                for (j = i - gap; j >= 0 && nums[j] > tmp; j -= gap)
                    nums[j + gap] = nums[j];
                nums[j + gap] = tmp;
            }
        }
    }



    //堆排序,不稳定排序
    //时间 平均2nlogn - O(nlogn)
    //空间O(1)
    //排序方式
//  创建一个堆H[0..n-1]
//  把堆首(最大值)和堆尾互换
//  把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置
//  重复步骤2,直到堆的尺寸为1
    public void heapSort(int[] nums){
        int i;
        //遍历所有结点,对不满足规则节点进行调整   
        for(i = nums.length / 2;i >= 0;i--){
            PercDown(nums, i,nums.length);
        }
        for(i= nums.length - 1;i > 0;i--){
            int tmp = nums[0];
            nums[0] = nums[i];
            nums[i] = tmp;
            PercDown(nums, 0, i);
        }
    }
    //调整堆序
    public void PercDown(int[] nums, int i, int length){
        int child;
        int tmp;

        for(tmp = nums[i]; 2*i + 1 < length;i = child){
            child =  2*i + 1;   //左儿子
            if(child != length - 1 && nums[child + 1] > nums[child])
                child++;        //选出左右子结点中的较大值
            if (tmp < nums[child]) {    //父节点小于子节点,需要进行调整
                nums[i] = nums[child];
            }
            else
                break;
        }
        nums[i] = tmp;
    }

    //归并排序
    public void mergeSort(int[] nums){
        int[] tmpArray = new int[nums.length];
        mSort(nums,tmpArray,0,nums.length - 1);
    }
    public void mSort(int[] nums,int[] tmps,int left,int right){
        int center;
        if(left < right){
            center = (left + right) / 2;
            mSort(nums, tmps, left, center);
            mSort(nums, tmps, center + 1, right);
            merge(nums,tmps,left,center + 1,right);
        }
    }
    //空间归并
    public void merge(int[] nums,int[] tmps,int lpos,int rpos,int rightEnd){
        int i,leftEnd,numElements,tmpPos;
        leftEnd = rpos - 1;
        tmpPos = lpos;
        numElements = rightEnd - lpos + 1;
        while(lpos <= leftEnd && rpos <= rightEnd){
            if(nums[lpos] <= nums[rpos])
                tmps[tmpPos++] = nums[lpos++];
            else
                tmps[tmpPos++] = nums[rpos++];
        }
        while(lpos <= leftEnd)
            tmps[tmpPos++] = nums[lpos++];
        while(rpos <= rightEnd)
            tmps[tmpPos++] = nums[rpos++];
        for(i = 0;i < numElements;i++,rightEnd--)
            nums[rightEnd] = tmps[rightEnd];
    }

    //快速排序
    //理论上能保证不出现最坏情况:三数中值分割法
    //小规模排序,快速排序不如插入排序
    public void quickSort(int[] nums){
        qSort(nums,0,nums.length - 1);
    }
    //递归快速排序
    public void qSort(int[] nums,int left,int right){
        int i,j;
        int pivot;

        if(left + 3 <= right){  //至少有4个数
            pivot = median3(nums, left, right);
            i = left;
            j = right - 1;
            while(true){
                //先推进索引再判断,而不是先判断再决定要不要推进索引
                //这样避免了a[i]=a[j]=pivot,导致索引不会被推进,也不会跳出循环
                while(nums[++i] < pivot){}
                while(nums[--j] > pivot){}
                if(i < j){
                    int tmp = nums[i];
                    nums[i] = nums[j];
                    nums[j] = tmp;
                }
                else
                    break;
            }
            int tmp = nums[i];
            nums[i] = nums[right-1];
            nums[right-1] = tmp;
            qSort(nums, left, i - 1);
            qSort(nums, i + 1, right);
        }
        //少于4个数,不再分割,用插入排序直接排序
        else {
            int k,p;
            int tmp;
            for(p = 1;p < right - left + 1; p++){
                tmp = nums[left+p];
                for(k = p;k > 0;k--){
                    if(nums[left+k-1] > tmp)
                    nums[left+k] = nums[left+k-1];
                }
                nums[left+k] = tmp;
            }
        }
    }
    //分割数组
    public int median3(int[] nums,int left,int right){
        int center = (left + right) / 2;

        if(nums[left] > nums[center]){
            int tmp = nums[left];
            nums[left] = nums[center];
            nums[center] = tmp;
        }
        if(nums[left] > nums[right]){
            int tmp = nums[left];
            nums[left] = nums[right];
            nums[right] = tmp;
        }
        if(nums[center] > nums[right]){
            int tmp = nums[center];
            nums[center] = nums[right];
            nums[right] = tmp;
        }
        int tmp = nums[center];
        nums[center] = nums[right - 1];
        nums[right - 1] = tmp;
        return nums[right - 1];
    }


}


















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值