希尔排序(更高效的插入排序)
减少最小数在最后一位的情况下要循环的次数
思路:
把数组按增量(n/2)分组,对每一组使用插入排序去排序交换位置,然后不停地增量/2,直到其为1时,结束
- 分组:如n/2=5
891723
8与3为一组
从不包含本身的数开始数 - 两种实现方法:
交换法(效率较低)
移动法(效率较高)
交换法
对第一轮排序的分析
public class ShellSort {public static void main(String[] args) {int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};System.out.println("排序前---");System.out.println(Arrays.toString(arr));shellSort(arr);}public static void shellSort(int[] arr) {//第一种方式:交换法//10个数据进行了三轮排序//第一轮,将1o个数据分成了5组//遍历每一组,共有5组for (int i = 5; i < arr.length; i++) {//遍历各组中所有的元素(共有5组),每一组有两个元素,步长5//int j = i-5刚好是每一组的第一个元素//j -= 5为了退出当前循环,进行下一组交换for (int j = i - 5; j >= 0; j -= 5) {//如果当前元素大于加上步长后的元素,说明需要交换if (arr[j] > arr[j + 5]) {//交换int temp = arr[j];arr[j] = arr[j + 5];arr[j + 5] = temp;}}}System.out.println("第1轮插入后---");System.out.println(Arrays.toString(arr));}
}
排序过程
排序前---
[8, 9, 1, 7, 2, 3, 5, 4, 6, 0]
第1轮插入后---
[3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
第2轮插入后---
[0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
第3轮插入后---
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
代码实现:
public class ShellSort {public static void main(String[] args) {int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};System.out.println("排序前---");System.out.println(Arrays.toString(arr));shellSort(arr);}public static void shellSort(int[] arr) {//第一种方式:交换法//10个数据进行了三轮排序//第一轮,将1o个数据分成了5组//遍历每一组,共有5组//gsp每一次的增量,最后为1int count = 0;for (int gsp = arr.length / 2; gsp > 0; gsp /= 2) {for (int i = gsp; i < arr.length; i++) {//遍历各组中所有的元素(共有5组),每一组有两个元素,步长5//int j = i-5刚好是每一组的第一个元素//j -= 5为了退出当前循环,进行下一组交换for (int j = i - gsp; j >= 0; j -= gsp) {//如果当前元素大于加上步长后的元素,说明需要交换if (arr[j] > arr[j + gsp]) {//交换int temp = arr[j];arr[j] = arr[j + gsp];arr[j + gsp] = temp;}}}System.out.println("第" + (++count) + "轮插入后---");System.out.println(Arrays.toString(arr));}}
}
移动法
交换法效率低是因为发现一个就交换一个
public class ShellSort {public static void main(String[] args) {int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};System.out.println("排序前---");System.out.println(Arrays.toString(arr));shellSort2(arr);}public static void shellSort2(int[] arr) {//第二种方法:移动法//使用增量,逐步缩小增量int count = 0;for (int gap=arr.length/2;gap>0;gap/=2){//从第gap个元素开始,逐个对其所在组进行直接插入排序//遍历每一个组for (int i = gap; i < arr.length; i++) {int index=i;//待插入的下标,每个组的第二个元素int value=arr[index];//用临时变量记录要插入的数//找位置arr[index-gap]每个组第一个元素if(arr[index]<arr[index-gap]){while (index-gap>=0&&value<arr[index-gap]){//移动arr[index]=arr[index-gap];index-=gap;}//当退出while就找到了插入的位置arr[index]=value;}}System.out.println("第" + (++count) + "轮插入后---");System.out.println(Arrays.toString(arr));}}}
无注释版
public class TestShellSort {public static void main(String[] args) {int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};System.out.println("排序前---");System.out.println(Arrays.toString(arr));shellSort2(arr);}private static void shellSort1(int[] arr) {//交换法for (int gap = arr.length / 2; gap > 0; gap /= 2) {for (int i = gap; i < arr.length; i++) {for (int j = i - gap; j >= 0; j -= gap) {if (arr[j] > arr[j + gap]) {int temp = arr[j];arr[j] = arr[j + gap];arr[j + gap] = temp;}}}}System.out.println("排序后---");System.out.println(Arrays.toString(arr));}private static void shellSort2(int[] arr) {
//移动法for (int gap = arr.length / 2; gap > 0; gap /= 2) {for (int i = gap; i < arr.length; i++) {int index=i;int value=arr[index];if (arr[index]<arr[index-gap]){while (index-gap>=0&&value<arr[index-gap]){arr[index]=arr[index-gap];index-=gap;}arr[index]=value;}}}System.out.println("排序后---");System.out.println(Arrays.toString(arr));}
}