希尔排序(更高效的插入排序)
减少最小数在最后一位的情况下要循环的次数
思路:
把数组按增量(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每一次的增量,最后为1
int 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));
}
}