本文带来八大排序算法之希尔排序。
希尔排序可以认为是插入排序的一种优化算法。在上一篇插入排序的举例数组中,当第二轮排序过后,数组变为[34, 101, 119, 1],此时如果将最后一个元素放到最前,则要经历如下过程:
[34, 101, 119, 119]
[34, 101, 101, 119]
[34, 34, 101, 119]
[1, 34, 101, 119]
结论:当需要插入的数是较小的数时,数组前方的元素后移次数明显增多,对效率有影响。
希尔排序也是一种插入排序,它是简单插入排序经过改进后的一个更高效的版本,也称为缩小增量排序。希尔排序是把记录按下标的一定增量(gap)分组,对每组使用直接插入排序算法排序 ;随着增量的逐渐缩小,每组包含的元素越来越多,当增量缩减至1时,整个数组恰好被分成一组,算法结束。
最后一轮排序后结果如下:
希尔排序有交换法和移位法两种方法,交换法效率较低,移位法效率较高。现在分别实现如下两种方法,并给出希尔排序的推导过程,数组以 {8,9,1,7,2,3,5,4,6,0}为例
推导过程如下:
数组长度为10,因此第一次gap = 10/2 = 5
//推导过程
int temp = 0;
//第1轮排序,将10个数据分成5组
for(int i=5; i<arr.length; i++){ //此循环只是为了将数组分为5组
//遍历各组中的所有元素(共有5组,每组2个元素,步长5、(j = j-5,意味着从后往前数)
for(int j=i-5; j>=0; j -= 5){
//如果当前元素大于加上步长后的那个元素,说明则需要交换
if(arr[j] > arr[j+5]){
temp = arr[j];
arr[j] = arr[j+5];
arr[j+5] = temp;
}
}
}
System.out.println("希尔排序第1轮= " + Arrays.toString(arr));
//第2轮排序,将10个数据分成5/2 = 2组
for(int i=2; i<arr.length; i++){ //此循环只是为了将数组分为2组
//遍历各组中的所有元素(共有2组,每组5个元素,步长2
for(int j=i-2; j>=0; j -= 2){
//如果当前元素大于加上步长后的那个元素,说明则需要交换
if(arr[j] > arr[j+2]){
temp = arr[j];
arr[j] = arr[j+2];
arr[j+2] = temp;
}
}
}
System.out.println("希尔排序第2轮= " + Arrays.toString(arr));
//第3轮排序,将10个数据分成2/2 = 1组
for(int i=1; i<arr.length; i++){ //此循环只是为了将数组分为1组
//遍历各组中的所有元素
for(int j=i-1; j>=0; j -= 1){
//如果当前元素大于加上步长后的那个元素,说明则需要交换
if(arr[j] > arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println("希尔排序第3轮= " + Arrays.toString(arr));
交换法代码实现:
public static void shellSort(int[] arr){
//希尔排序 第一轮排序
int temp = 0;
int cnt = 0;
for(int gap = arr.length/2; gap > 0; gap /= 2){
for(int i=gap; i<arr.length; i++){
//遍历各组中的所有元素(共有gap组,步长gap、(j = j-gap,意味着从后往前数))
for(int j=i-gap; j>=0; j -= gap){
if(arr[j] > arr[j+gap]){
temp = arr[j];
arr[j] = arr[j+gap];
arr[j+gap] = temp;
}
}
}
System.out.println("希尔排序第" + (++cnt) + "轮:" + Arrays.toString(arr));
}
}
移位法代码实现(在交换时,采取只交换一次的方式):
public static void shellSort2(int[] arr){
int cnt = 0;
//增量为gap, 并逐步缩小增量
for(int gap = arr.length/2; gap > 0; gap /= 2){
for(int i=gap; i<arr.length; i++){
int insertIndex = i;
int insertValue = arr[insertIndex];
if(arr[insertIndex] < arr[insertIndex - gap]){
while(insertIndex - gap >=0 && insertValue < arr[insertIndex - gap]){
arr[insertIndex] = arr[insertIndex - gap];
insertIndex -= gap;
}
//当退出while后,就给insertValue找到了插入的位置
arr[insertIndex] = insertValue;
}
}
System.out.println("移位-希尔排序第" + (++cnt) + "轮:" + Arrays.toString(arr));
}
}