相比于速度较慢的简单排序---冒泡排序、插入排序和选择排序,在之前也介绍了一种使用递归算法的归并排序(速度虽快,但空间消耗大,需要数组大小的辅助空间)。希尔排序和快速排序是我们常用的两种高级排序算法,其中希尔排序的时间复杂度为O(N*(logN)2),快速排序的时间复杂度为O(N*logN),而且和归并排序不一样,不需要那么大的辅助空间。
一、希尔排序
希尔排序基于插入排序,但新增一个特性,可以使性能得到很大的提升。希尔排序对几千个数据量的,中等规模的排序是比较适用的。在最坏和平均条件情况下,执行的效果基本上一样。其实你可以将希尔排序看作N-增量排序算法。同时在每一趟排序结束后将N减小,直到N等于1。一般使用的算法为:N=(N-1)/3。
希尔排序实现代码如下:
1 /** 2 * 针对array数组进行shell排序,排序的增量计算采用:h = (h-1)/3 3 * 4 * @param array 5 */ 6 public static void shellSort(double[] array) { 7 if (array == null || array.length == 0) { 8 return; 9 } 10 11 int nItems = array.length; 12 int h = 1; 13 while (h < nItems / 3) { 14 h = h * 3 + 1; 15 } 16 17 while (h > 0) { 18 for (int i = h; i < nItems; i++) { 19 double tmpValue = array[i]; 20 int j = i; 21 while (j > h - 1 && array[j - h] > tmpValue) { 22 array[j] = array[j - h]; 23 j -= h; 24 } 25 array[j] = tmpValue; 26 } 27 h = (h - 1) / 3; 28 } 29 }
调用希尔排序的代码以及结果如下:
1 double[] array; 2 array = new double[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; 3 System.out.println(Arrays.toString(array)); 4 ShellSort.shellSort(array); 5 System.out.println(Arrays.toString(array)); 6 System.out.println("********************"); 7 8 9 结果是: 10 [10.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0] 11 [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
希尔排序中除了采用h=(h-1)/3这种序列外,还可以采用其他序列,唯一的要求就是,最后一个序列必须是1,及普通的插入排序。比如h=h/2,h=h/2.2等,其中第一种中,针对某些情况下,性能可能比插入排序还要差。一般而言,序列间的数字互质是非常重要的,也就是除了公约数1外,没有其他公约数,这样的序列可以更好的保证每一趟的排序能够保持前一次已排序好的效果。其中以h/2这种序列就违背了这种约束。还有一种比较常用的序列方案是:h=1(h<5);h=(5h-1)/11(h>=5)
二、划分
划分是快速排序的基础,划分的意思是说,将数组分成两部分,第一部分的数据是小于等于标识位的数据的,第二部分的数据是大于标志位的数据的。
1 public static int partitionTwice(int[] array, int left, int right, int pivot) { 2 if (left > right) { 3 return -1; 4 } else { 5 int leftPro = left; 6 int rightPro = right; 7 8 while (true) { 9 while (leftPro < rightPro && array[leftPro] < pivot) { 10 leftPro++; 11 } 12 13 while (rightPro > leftPro && array[rightPro] >= pivot) { 14 rightPro--; 15 } 16 17 if (leftPro == rightPro) { 18 break; 19 } 20 21 int tmpValue = array[leftPro]; 22 array[leftPro] = array[rightPro]; 23 array[rightPro] = tmpValue; 24 } 25 26 return leftPro == right ? right + 1 : leftPro; 27 } 28 }
执行代码:
1 class PartitionApp { 2 public static void main(String[] args) { 3 int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; 4 System.out.println(array.length); 5 6 System.out.println(Arrays.toString(array)); 7 int k = Partition.partitionOnce(array, 0, array.length, 4); 8 System.out.println(Arrays.toString(array)); 9 System.out.println(k); 10 } 11 } 12 13 22 14 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] 15 [1, 2, 3, 4, 1, 2, 3, 4, 0, 10, 11, 9, 10, 9, 8, 7, 6, 5, 8, 7, 6, 5] 16 8
三、快速排序
快速排序是针对冒泡排序的一种改进。快速排序的实质是将要排序的数组分成两部分,第一部分的数据全部小于第二部分的数据,然后分别迭代这俩部分的数据即可。
实现的代码如下所示:
1 public class QuickSort { 2 public static void quickSortByRecursive(int[] array) { 3 if (array == null || array.length == 0) { 4 return; 5 } else { 6 quickSortByRecursive(array, 0, array.length - 1); 7 } 8 } 9 10 public static void quickSortByRecursive(int[] array, int left, int right) { 11 if (right - left <= 3 || left == -1 || right == array.length) { 12 int midValue = array[(left + right) / 2]; 13 int maxValue = Math.max(array[left], Math.max(midValue, array[right])); 14 int minValue = Math.min(array[left], Math.min(midValue, array[right])); 15 midValue = (array[left] + midValue + array[right]) - maxValue - minValue; 16 array[left] = minValue; 17 array[right] = maxValue; 18 array[(right + left) / 2] = midValue; 19 return; 20 } else { 21 int pivot = getPivot(array, left, right); 22 int partition = partitionIt(array, left, right, pivot); 23 quickSortByRecursive(array, left, partition - 1); 24 quickSortByRecursive(array, partition, right); 25 } 26 } 27 28 private static int getPivot(int[] array, int left, int right) { 29 int mid = (left + right) / 2; 30 if (array[left] > array[mid]) { 31 if (array[right] > array[left]) { 32 return array[left]; 33 } else if (array[right] > array[mid]) { 34 return array[right]; 35 } else { 36 return array[mid]; 37 } 38 } else { 39 if (array[right] > array[mid]) { 40 return array[mid]; 41 } else if (array[right] > array[left]) { 42 return array[right]; 43 } else { 44 return array[left]; 45 } 46 } 47 } 48 49 private static int partitionIt(int[] array, int left, int right, int pivot) { 50 return Partition.partitionTwice(array, left, right, pivot); 51 } 52 }
使用快速排序以及结果如下:
1 class QuickSortApp { 2 public static void main(String[] args) { 3 int[] array; 4 array = new int[] { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 }; 5 System.out.println(Arrays.toString(array)); 6 QuickSort.quickSortByRecursive(array); 7 System.out.println(Arrays.toString(array)); 8 } 9 } 10 11 [1, 3, 5, 7, 9, 2, 4, 6, 8, 10] 12 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]