希尔排序是插入排序的一种,又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本
插入排序存在的效率问题
如果已经排序的分组元素为{2,5,7,9,10}未排序的分组元素为{1,8}那么下一个待插入元素为1,我们需要拿着1从后往前,一次和10,9,7,5,2进行位置交换,才能真正完成插入。如果我们要提高效率,就是把1放到更前面的位置,例如直接将1放到2前面,这样可减少交换次数。下面我们来看看希尔排序
将数组{9,1,2,57,4,8,6,3,5}正序
希尔排序原理
1、选定一个增长量,按照增长量h作为数组分组的依据,对数据进行分组
2、对分好组的每一组数据完成插入排序
3、减小增长量,最小减为1,重复第二部操作
第一次h应该为7,这里为了方便的分析排序过程,暂时写作5
增长量h的确定:增长量h的值固定规则如下
int h=1;
while(h<数组长度/2){
h=2h+1
}
增长量减少规则
h=h/2 如果除不开按照java的除法规则,例如7/2=3
代码
public static void main(String[] args) { Integer arr[] = {9,1,2,57,4,8,6,3,5}; sort(arr); System.out.println(Arrays.toString(arr)); sort(arr, Comparator.reverseOrder()); System.out.println(Arrays.toString(arr)); } /** * 自定义排序 * * @param arr * @param comparator * @param <T> */ public static <T> void sort(T[] arr, Comparator<T> comparator) { int h=1; while (h<arr.length/2) { h = 2 * h + 1; } while (h>=1){ for (int i = h; i <= arr.length-1; i+=h) { for (int j = i; j >=h; j-=h) { if(greater(comparator,arr[j],arr[j-h])){ exch(arr,j-h,j); }else { break; } } } h=h/2; } } /** * 正序 * * @param arr * @param <T> */ public static <T> void sort(Comparable<T>[] arr) { //找到最大增量 int h=1; while (h<arr.length/2) { h = 2 * h + 1; } //当增量为1的时候所有数据都在一组 while (h>=1){ //找到待排序元素,通过观察发现待排序元素的索引=增长量 所以i=h //arr.length-1 参与排序的最大索引 i+=h 因为待排序索引的开始值 for (int i= h; i <= arr.length-1; i+=h) { //假如h=2 待排序索引为8,通过已排好序的数组的索引6,4,2,0,发现每次都-h, //所以j=j-h,j>=h是因为0索引在if判断和位置交换的j-h索引处,否则会出现负数角标 //j=i 因为待排序的索引是变化的,而这种变化是通过i体现出来的 for (int j = i; j >=h; j-=h) { if(greater(arr[j-h],arr[j])){ exch(arr,j,j-h); }else { break; } } } h=h/2; } } /** * 比较大小,如果c1比c2大,返回true,否则false,正序 * * @param c1 * @param c2 * @return */ private static boolean greater(Comparable c1, Comparable c2) { return c1.compareTo(c2) > 0 ? Boolean.TRUE : Boolean.FALSE; } private static <T> boolean greater(Comparator comparator, T c1, T c2) { return comparator.compare(c1,c2 ) > 0 ? Boolean.TRUE : Boolean.FALSE; } /** * 将arr数组中i角标和j角标位置互换 * * @param arr 数组 * @param i 角标 * @param j 角标 * @param <T> */ private static <T> void exch(T[] arr, int i, int j) { T temp; temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }
时间复杂度分析
在希尔排序中,增长量h并没固定的规则,很多论文中研究了不同的递增序列,但都无法证明某个序列是最好的,对于希尔排序的时间复杂度分析,设计很多数学知识,这里不做分析了。
所以我们采用事后分析法对希尔排序和插入排序的性能做比较
希尔排序最坏情况事后分析法
public class Shell { public static void main(String[] args) { Integer arr[] = new Integer[10000000]; for (int i = 10000000-1; i >=0 ; i--) { arr[i]=i; } System.out.println(Arrays.toString(arr)); LocalDateTime start = LocalDateTime.now(); sort(arr); LocalDateTime end = LocalDateTime.now(); System.out.println(Arrays.toString(arr)); System.out.println("希尔排序的时间:"+ Duration.between(start,end).getNano()); } /** * 正序 * * @param arr * @param <T> */ public static <T> void sort(Comparable<T>[] arr) { int h=1; while (h<arr.length/2) { h = 2 * h + 1; } while (h>=1){ for (int i= h; i <= arr.length-1; i+=h) { for (int j = i; j >=h; j-=h) { if(greater(arr[j-h],arr[j])){ exch(arr,j,j-h); }else { break; } } } h=h/2; } } /** * 比较大小,如果c1比c2大,返回true,否则false,正序 * * @param c1 * @param c2 * @return */ private static boolean greater(Comparable c1, Comparable c2) { return c1.compareTo(c2) > 0 ? Boolean.TRUE : Boolean.FALSE; } /** * 将arr数组中i角标和j角标位置互换 * * @param arr 数组 * @param i 角标 * @param j 角标 * @param <T> */ private static <T> void exch(T[] arr, int i, int j) { T temp; temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } }
插入排序最坏情况时候分析法
public class Insertion { public static void main(String[] args) { Integer arr[] = new Integer[10000000]; for (int i = 10000000-1; i >=0 ; i--) { arr[i]=i; } System.out.println(Arrays.toString(arr)); LocalDateTime start = LocalDateTime.now(); sort(arr); LocalDateTime end = LocalDateTime.now(); System.out.println(Arrays.toString(arr)); System.out.println("插入排序的时间:"+ Duration.between(start,end).getNano()); } /** * 正序 * * @param arr * @param <T> */ public static <T> void sort(Comparable<T>[] arr) { for (int i = 1; i <arr.length ; i++) { for (int j = i; j >0 ; j--) { if(greater(arr[j-1],j)){ exch(arr,j-1,j); }else { break; } } } } /** * 比较大小,如果c1比c2大,返回true,否则false,正序 * * @param c1 * @param c2 * @return */ private static boolean greater(Comparable c1, Comparable c2) { return c1.compareTo(c2) > 0 ? Boolean.TRUE : Boolean.FALSE; } private static <T> boolean greater(Comparator comparator, T c1, T c2) { return comparator.compare(c1,c2 ) > 0 ? Boolean.TRUE : Boolean.FALSE; } /** * 将arr数组中i角标和j角标位置互换 * * @param arr 数组 * @param i 角标 * @param j 角标 * @param <T> */ private static <T> void exch(T[] arr, int i, int j) { T temp; temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } }
希尔排序的时间:178000000
插入排序的时间:273000000