6.6 希尔排序
6.6.1 插入排序的问题所在
对于插入排序存在一个问题: 如果待排序列前面的数据已经有序,而恰巧靠后的数字又比较小,后面的数据就要一直比较到最前面,这样需要比较的次数比较多;
希尔排序解决这一问题: 把整个待排序列进行增量分组,对每组使用插入排序算法,没排序完,使得增量减半,随着增量的逐渐减小至1,整个数组被分成一组进行排序,而此时大部分数据已经处于有序位置,
即使无序数据也离正确的位置不远,比较次数少。
6.6.2 希尔排序的思想
插入排序时比较相邻的元素大小,进行插入,希尔排序是在插入排序的基础上,对所有待排序以一个间隔进行插入排序,间隔gap的初始化值可以设置为数组长度的一半,每当一轮以gap为单位的插入排序结束之后,减小gap的值,直至gap等于1,此时最后一轮排序时,整个数组的大部分元素已经处于有序位置,进行最后一轮插入排序。可以理解为希尔排序是在插入排序的基础上进行的。
6.6.3 希尔排序示意图
6.6.4 代码分析:
package com.kevin.sortAlgorithm;
import java.util.Arrays;
/**
* @author : kevin ding
* @date : 2022/3/3 21:47
* @description : 对于插入排序存在一个问题: 如果待排序列前面的数据已经有序,而恰巧靠后的数字又比较小,后面的数据就要一直比较到最前面,这样需要比较的次数比较多;希尔排序解决这一问题: 把整个待排序列进行增量分组,对每组使用插入排序算法,没排序完,使得增量减半,随着增量的逐渐减小至1,整个数组被分成一组进行排序,而此时大部分数据已经处于有序位置,即使无序数据也离正确的位置不远,比较次数少
*/
public class ShellSortDemo {
public static void main(String[] args) {
// int[] array = {8,9,1,7,2,3,5,4,6,0};
// System.out.println("排序之前,结果为:");
// System.out.println(Arrays.toString(array));
// // shellSortAnalysis(array);
// shellSort(array);
// 对于 100000个数据进行排序,求其时间
int[] array = new int[80000];
for (int i = 0; i < array.length; i++) {
array[i] = (int) (Math.random() * 8000000);
}
Long startTime = System.currentTimeMillis();
shellSort(array);
Long endTime = System.currentTimeMillis();
System.out.println("希尔排序 所耗费的时间是:" + (endTime-startTime) + "ms"); // 12ms
}
public static void shellSortAnalysis(int[] array){
// 对于一组数据,首先指定增量初始值为数组长度的一半 gap = 10/2 =5;
// 下标分组情况为 0,5; 1 6; 2 7; 3 8; 4 9 对这5组数据分别进行插入排序
// int temp; // 临时变量用于数据交换
int insertValue;
int insertIndex;
// 第一轮排序:将10个数据分成了5组 需要遍历从gap之后的数 步长为gap
int gap = 5;
// 从 array[gap]到array[n-1]的元素为带插入元素,前面的有序元素为分组
for(int i=gap; i<array.length; i++){
//
insertIndex = i;
insertValue = array[i];
// 此时需要进行比较的元素是与insertIndex相差gap的一个元素 比前一个gap的元素小,则需要进行交换
// 若insertIndex小于gap了,则说明其前面没有相距gap的值了,已经处于了这组中合适的位置
while(insertIndex >=gap && array[insertIndex - gap] > insertValue){
// 需要进行交换
array[insertIndex] = array[insertIndex- gap];
array[insertIndex-gap] = insertValue;
insertIndex -= gap;
}
}
System.out.println("第1 轮shell排序之后:");
System.out.println(Arrays.toString(array));
// 第2轮排序:将10个数据分成了5/2 = 2组 需要遍历从gap之后的数 步长为gap
gap = 2;
// 从 array[gap]到array[n-1]的元素为带插入元素,前面的有序元素为分组
for(int i=gap; i<array.length; i++){
//
insertIndex = i;
insertValue = array[i];
// 此时需要进行比较的元素是与insertIndex相差gap的一个元素 比前一个gap的元素小,则需要进行交换
// 若insertIndex小于gap了,则说明其前面没有相距gap的值了,已经处于了这组中合适的位置
while(insertIndex >=gap && array[insertIndex - gap] > insertValue){
// 需要进行交换
array[insertIndex] = array[insertIndex- gap];
array[insertIndex-gap] = insertValue;
insertIndex -= gap;
}
}
System.out.println("第1 轮shell排序之后:");
System.out.println(Arrays.toString(array));
// 第3轮排序:将10个数据分成了2/2 = 1组 需要遍历从gap之后的数 步长为gap
gap = 1;
// 从 array[gap]到array[n-1]的元素为带插入元素,前面的有序元素为分组
for(int i=gap; i<array.length; i++){
//
insertIndex = i;
insertValue = array[i];
// 此时需要进行比较的元素是与insertIndex相差gap的一个元素 比前一个gap的元素小,则需要进行交换
// 若insertIndex小于gap了,则说明其前面没有相距gap的值了,已经处于了这组中合适的位置
while(insertIndex >=gap && array[insertIndex - gap] > insertValue){
// 需要进行交换
array[insertIndex] = array[insertIndex- gap];
array[insertIndex-gap] = insertValue;
insertIndex -= gap;
}
}
System.out.println("第1 轮shell排序之后:");
System.out.println(Arrays.toString(array));
}
}
6.6.5 代码实现:
public static void shellSort(int[] array){
// 上述的逐次过程中,只有gap在发生变化,可以进行循环
// 定义待比较元素值 和 该元素合适的位置索引
int insertValue;
int insertIndex;
int count = 0;
// 第一轮排序:将10个数据分成了5组 需要遍历从gap之后的数 步长为gap
int gap = array.length / 2;
while(gap > 0){
// 从 array[gap]到array[n-1]的元素为带插入元素,前面的有序元素为分组
for(int i=gap; i<array.length; i++){
//
insertIndex = i;
insertValue = array[i];
// 此时需要进行比较的元素是与insertIndex相差gap的一个元素 比前一个gap的元素小,则需要进行交换
// 若insertIndex小于gap了,则说明其前面没有相距gap的值了,已经处于了这组中合适的位置
while(insertIndex >=gap && array[insertIndex - gap] > insertValue){
// 需要进行交换
array[insertIndex] = array[insertIndex- gap];
array[insertIndex-gap] = insertValue;
insertIndex -= gap;
}
}
// 一轮循环结束,gap的值减半
gap /= 2;
}
}