记得学习算法的时候还是在大二开的数据结构课程与算法课程上学的,当初对算法甚是感兴趣,当初对那些提出算法的牛人甚是崇拜。可是后面的学习中却很少用到算法,渐渐的淡忘啦。如今快毕业了,突然就想在毕业之前把自己在以前遗忘的算法好好复习一下,就买了一本Robert Sedgewick写的《算法》,这本书相对于《算法导论》个人感觉更易于理解。
感觉学完算法之后,最大的收获就是"分治"的思想。
今天要写的是希尔排序,一种基于插入排序的快速的排序算法。我们知道 插入排序 对于大规模乱序数字插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点点地从数组一段移动到另一段。例如,如果主键最小的元素正好在数组的尽头,要将它挪到正确的位置就需要 N-1 次移动。希尔排序为了加快速度简单地该井了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序讲局部有序的数组排序。
希尔排序的思想就是使数组中任意间隔为h的元素都是有序的。这样的数组被称为h有序数组。换句话说,一个h有序数组就是h个互相独立的有序数组编制在一起组成的一个数组。
实现希尔排序的一种方法是对于每个h,用插入排序讲h个子数组独立的排序。但因为子数组是相互独立的,一个更简单的方法是在h子数组中将每个元素交换到比它大的元素之前(将比它大的元素向右移动一格)。只需要在插入排序的代码中将移动的元素距离由1改为h即可。这样希尔排序的实现就转化为一个类似于插入排序但使用不同增量的过程。
1 public static void shell_sort(int[] a) { 2 int N = a.length; 3 int d = N / 2;// 增量初始化为数组长度的一般,以后每次减半,直到增量为1。 4 while (d >= 1) { 5 for (int i = d; i < N; i++) { 6 for (int j = i; j >= d; j -= d) { 7 if (a[j] < a[j - d]) { 8 int t = a[j]; 9 a[j] = a[j - d]; 10 a[j - d] = t; 11 } 12 } 13 } 14 d = d / 2;//直到增量为1。 15 } 16 }
增量的选择对算法的性能影响很大,有很多论文研究了各种不同的递增系列,但都无法证明某个"序列" 是最好的。
在贴出书本上算法序列的选择的算法
1 public static <T> boolean less(Comparable v, Comparable w) { 2 return v.compareTo(w) < 0; 3 } 4 5 public static void exch(Object[] a, int i, int j) { 6 Object temp = a[i]; 7 a[i] = a[j]; 8 a[j] = temp; 9 } 10 11 public static <T> void sort(Comparable<T>[] a) { 12 int N = a.length; 13 int h = 1; 14 while (h < N / 3) 15 h = 3 * h + 1;// 1, 4, 13, 40 16 while (h >= 1) { 17 for (int i = h; i < N; i++) 18 //在子数组中进行插入排序将a[i]插入到a[j-h], a[i-2*h], a[i-3*h]....之中 19 for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) { 20 exch(a, j, j - h); 21 } 22 h = h / 3; 23 } 24 }
使用10W个数字进行测试 如果选择第一种序列 使用 22754 微秒,第二种序列使用 161 微妙 差距都一个数量级啦,序列的选择对性能影响还是很大的。