手撕面试题算法<排序>(3.5)—— 希尔排序

前言

之所以把希尔排序作为排序系列的第3.5篇,是因为希尔排序可以称得上是 插入排序Plus ,它在插入排序的基础上继续升级,突破了时间复杂度O(n2)的壁障

手撕算法 - 排序系列

源码

希尔排序

思想

    希尔排序中,有一个叫做增量的概念,在排序过程中需要按照增量分组 说白了,就是在一个数组中跳着选数组元素,归为一组,再将每个组进行直接插入排序 在所有组的直接插入排序完成后,将增量按照一定的规则缩小,再将上述操作进行一遍,直到增量最终小于1

假如传入了一个大小为10的随机数组[7,3,4,1,8,9,2,0,6,5]

    让增量初始化为数组长度10,incr = 10 排序开始,我们假定增量缩小的规则为每次让增量折半,即incr/=2,此时incr = 5 我们将传入的一维数组看作n行5列的二维数组: [7,3,4,1,8] [9,2,0,6,5] 此时,我们将每一列归为一组,按组分为: [7,9] [3,2] [4,0] [1,6] [8,5] 再将这些组中所有出现逆序的组进行插入排序 一趟希尔排序完毕,将增量incr/=2,此时incr = 2 重复以上操作直到incr<1

时间复杂度

希尔排序的时间复杂度取决于传入数组和增量策略的设置,一般来说,其时间复杂度范围在O(n*logn)~O(n2),平均时间复杂度为O(n1.5)

实现

public static void sort(int[] arr) {
          
   
        int len = arr.length;
        int incr = len;
        while(incr>1){
          
   
            // 每趟希尔排序,都让增量的值折半
            incr/=2;
            for(int i=incr;i<len;++i){
          
   
                if(arr[i]<arr[i-incr]){
          
   
                	// 分组插入排序             	
            		// 当遍历时遇到相对有序,直接进行下一趟排序
                    for(int j=i;j>=incr && arr[j]<arr[j-incr];j-=incr){
          
   
                        Swaper.exec(arr,j,j-incr);
                    }
                }
            }
        }
    }

测试

排序测试:

由于希尔排序是时间性能突破了O(n2)的优秀排序算法,这次的测试用例数组直接上百万~

百万级数组耗时测试:

int n = 1_000_000;
    int[] tmp = Tester.randomArr(n);
    int[] arr = tmp.clone();
    System.out.println("对有" + n + "个元素的随机数组进行排序:");
    long start = System.currentTimeMillis();
    sort(arr);
    long end = System.currentTimeMillis();
    System.out.println("希尔排序结束,耗时" + (end - start) + "ms");

结果:

是不是比之前那些O(n2)排序要快得多了~

经验分享 程序员 微信小程序 职场和发展