希尔排序
插入排序的改进,定义一个增量gap,从第1个元素开始,每次增加gap个,然后用插入排序对这几个值排好序,下次我缩小gap,再对新的值排好序,直到gap=1,再用插入排序排好序。
public class ShellSort {
public static void main(String[] args) {
int[] arr = {9, 6, 1, 3, 5, 2, 4, 7, 8};
sort(arr);
}
public static void sort(int[] arr) {
// 记录比较次数
int num = 0;
// 记录交换次数
int swapNum = 0;
for (int gap = arr.length/2; gap > 0 ; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i; j > gap - 1; j -= gap) {
num++;
if (arr[j] < arr[j - gap]) {
swap(arr, j, j - gap);
swapNum ++;
} else {
break;
}
}
}
}
print(arr);
System.out.println("比较"+(num)+"次");
System.out.println("交换"+(swapNum)+"次");
}
/**
* 交换元素位置
* @param arr 数组
* @param i 位置1
* @param j 位置2
*/
static void swap(int[] arr, int i, int j) {
// 记录原最小值,用于和最小值互换
int temp = arr[i];
// 数组中原最小值换为新的最小值
arr[i] = arr[j];
// 最小值的位置换为原最小值
arr[j] = temp;
}
static void print(int arr[]) {
Arrays.stream(arr).forEach(num->{
System.out.print(num + " ");
});
System.out.println();
}
}
这样虽然比插入排序效率高,但这个变量gap值并不是一个最合适的值,如果取Knuth序列,效率将更高。
h = 1
h = 3 * h + 1
当最大间隔大于数组长度的 1 3 \frac{1}{3} 31,将不适用这个序列。
public class ShellSort1 {
public static void main(String[] args) {
int[] arr = {9, 6, 1, 3, 5, 2, 4, 7, 8};
sort(arr);
}
public static void sort(int[] arr) {
// 记录比较次数
int num = 0;
// 记录交换次数
int swapNum = 0;
// 找出数组最大的间隔
int h = 1;
while (h <= arr.length/3) {
h = h * 3 + 1;
}
// 间隔从最大的开始依次减小 前一个位置等于(h - 1) / 3
for (int gap = h; gap > 0; gap = (gap - 1) / 3) {
for (int i = gap; i < arr.length; i++) {
for (int j = i; j > gap - 1; j -= gap) {
num++;
if (arr[j] < arr[j - gap]) {
swap(arr, j, j - gap);
swapNum++;
} else {
break;
}
}
}
}
print(arr);
System.out.println("比较"+(num)+"次");
System.out.println("交换"+(swapNum)+"次");
}
/**
* 交换元素位置
* @param arr 数组
* @param i 位置1
* @param j 位置2
*/
static void swap(int[] arr, int i, int j) {
// 记录原最小值,用于和最小值互换
int temp = arr[i];
// 数组中原最小值换为新的最小值
arr[i] = arr[j];
// 最小值的位置换为原最小值
arr[j] = temp;
}
static void print(int arr[]) {
Arrays.stream(arr).forEach(num->{
System.out.print(num + " ");
});
System.out.println();
}
}
看一下希尔排序的时间复杂度,最外层循环,当gap趋近于n时,执行次数为 n ( n − 1 ) 3 \frac{n}{\frac{(n-1)}{3}} 3(n−1)n = 3 n n − 1 \frac{3n}{n-1} n−13n ≈ \approx ≈ 3,所以这个常数项可以忽略不计,再看第二层循环,大约执行n次,第三次循环最多的情况大约执行n次,所以最差时间复杂度为 O ( n 2 ) O(n^2) O(n2),最好情况和插入排序一样,数组是有序的话,内存循环只执行一次,所以最好时间复杂度为 O ( n ) O(n) O(n),程序没有用到额外的空间,所以空间复杂度为 O ( 1 ) O(1) O(1)。