1 概述
2 排序思想
一天,一尘拿着扑克自己在那玩,刚被师傅看见了
首先它把较大的数据集合分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高
可以看出,他是按下标相隔距离为4分的组,也就是说把下标相差4的分到一组,比如这个例子中a[0]与a[4]是一组、a[1]与a[5]是一组...,这里的差值(距离)被称为增量
每个分组进行插入排序后,各个分组就变成了有序的了(整体不一定有序)
此时,整个数组变的部分有序了(有序程度可能不是很高)
然后缩小增量为上个增量的一半:2,继续划分分组,此时,每个分组元素个数多了,但是,数组变的部分有序了,插入排序效率同样比高
同理对每个分组进行排序(插入排序),使其每个分组各自有序
最后设置增量为上一个增量的一半:1,则整个数组被分为一组,此时,整个数组已经接近有序了,插入排序效率高
同理,对这仅有的一组数据进行排序,排序完成
希尔排序动态演示
3 时间复杂度
4 希尔排序推导
//希尔排序推导 public static void main(String[] args) { int[] arr = {5,7,8,3,1,2,4,6}; shellSort(arr); } // 使用逐步推导的方式 public static void shellSort(int[] arr) { // 希尔排序第1轮 int temp; // 因为第一轮排序,将8个数据分成了4组 // 跨度为4 for (int i = 4; i < arr.length; i++) { // 遍历各种中所有的元素(共5组,每组2个元素) 步长5 for (int j = i - 4; j >= 0; j -= 4) { // 如果当前的元素大于步长后的那个元素 说明交换 if (arr[j] > arr[j + 4]) { temp = arr[j]; arr[j] = arr[j + 4]; arr[j + 4] = temp; } } } System.out.println("第1趟排序结果为:"); System.out.println(Arrays.toString(arr)); // 希尔排序第2轮 // 因为第一轮排序,将8个数据分成了4/2 = 2组 // 跨度为2 for (int i = 2; i < arr.length; i++) { // 遍历各种中所有的元素(共2组) 步长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趟排序结果为:"); System.out.println(Arrays.toString(arr)); // 希尔排序第3轮 // 因为第一轮排序,将10个数据分成了 2/2 = 1组 for (int i = 1; i < arr.length; i++) { // 遍历各种中所有的元素(共5组,每组2个元素) 步长5 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趟排序结果为:"); System.out.println(Arrays.toString(arr)); }
推导结果:
5 交换法实现希尔排序(代码方便理解,速度慢)
// **********************交换法******************************** // 根据前面的逐步分析,使用循环处理 public static void shellSort2(int [] arr) { //辅助变量 插入排序时进行交换 int temp; //通过循环 进行分组,每次的增量为长度的一半 for(int gap = arr.length / 2;gap > 0;gap /= 2) { //从gap开始 开始实现插入排序 for (int i = gap; i < arr.length; i++) { //循环数组前面的数组 进行比较 幅度变换为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; } } } } }
6 移位法实现希尔排序(代码难理解,速度快)
// **********************移位法************************************ public static void shellSort3(int[] arr) { // 增量gap 逐个对其所在的组进行直接插入排序 for (int gap = arr.length / 2; gap > 0; gap /= 2) { // 从第gap个元素,逐个对其所在的组进行直接插入排序 for (int i = gap; i < arr.length; i++) { int j = i; int temp = arr[j]; if (arr[j] < arr[j - gap]) { while (j - gap >= 0 && temp < arr[j - gap]) { // 移动 arr[j] = arr[j - gap]; j -= gap; } // 当退出这个循环时,就给temp找到插入的位置 arr[j] = temp; } } } }
7 希尔排序速度测试
public static void main(String[] args) { // *********************希尔数组 交换法速度测试*******************************888 int[] arr2 = new int[8000000]; for (int i = 0; i < arr2.length; i++) { arr2[i] = (int) (Math.random() * 800000); } // 显示排序前的数组 //System.out.println("选择排序测试数组:" + Arrays.toString(arr2)); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date1 = new Date(); String time1 = simpleDateFormat.format(date1); System.out.println("排序前的时间为:" + time1); //希尔排序 交换法 shellSort2(arr2); //希尔排序 移位法 shellSort3(arr2); Date date2 = new Date(); String time2 = simpleDateFormat.format(date2); System.out.println("排序后的时间为:" + time2); }
交换法-----速度测试结果:
8万数据排序结果(交换法) 大约需要10秒:
80万数据排序结果(交换法) 大约需要无数秒:(等不到结果了)
移位法-----速度测试结果:
8万数据排序结果(移位法) 需要不到1秒:
80万数据排序结果(移位法) 需要不到1秒:
800万数据排序结果(移位法) 需要不到3秒: