一/案例步骤分解
二/Java代码实现—— 锻炼逻辑思维,代码不是必要 还是挺重要的
三/还有一个问题—GAP怎么界定?
四/时间复杂度、空间复杂度及稳定性
希尔排序是D.L.Shell于1959年提出的因而得名。希尔排序就是插入排序的一种优化升级版,同时它是第一批在时间复杂度上突破O(n^2)的排序算法,所以意义深远。希尔排序会事先规定一个间隔值 gap 。假设有一个长度为n的数组arr,希尔排序会先对arr的子数组{arr[0],arr[gap],arr[gap2],…}进行简单插入排序,再对子数组{arr[1],arr[1+gap],arr[1+gap2],…}进行简单插入排序,这样依次进行直到子数组
{arr[gap-1],arr[gap2-1],arr[gap3-1],…}
接下来通过一个案例来说明吧。
假设定义一个数组int[] arr={9,8,7,6,5,4,3,2,1}如下,长度为9,这里先规定gap=4,其希尔排序流程如下 (颜色相同的是一组)
gap=4的情况就全部排完了,这时候令gap缩小为2,重复以上动作;
排完序是这样
这时候其实已经排完了,再令gap=1(就是简单插入排序了)走一遍流程,最后得出排好序的数组
public class ShellSortPra {
public static void main(String[] args) {
int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1};
sort(arr);
print(arr);
}
//希尔排序算法的排序主方法
static void sort(int[] arr) {
int gap;
for (gap = 4; gap > 0; gap /= 2) {//规定间隔为4,之后每次减半
for (int j = gap; j < arr.length; j++) {
for (int i = j; i > gap - 1; i -= gap) {
if (arr[i] < arr[i - gap]) {
swap(arr, i, i - gap);
System.out.println("gap=" + gap + " j=" + j + " i=" + i);
print(arr);
System.out.println("\n");
} else {
System.out.println("gap=" + gap + " j=" + j + " i=" + i);
print(arr);
System.out.println("\n");
}
}
}
}
}
//打印数组的方法封装
static void print(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
//交换元素的方法封装
static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
由于是直接插入排序的改造,所以我们基于直接插入排序的代码,来贴上
static void sort(int[] arr) {
for (int j = 1; j < arr.length; j++) {
for (int i = j; i > 0; i--) {
if (arr[i] < arr[i - 1]) {
swap(arr, i, i - 1);
}
}
}
}
static void sort(int[] arr) {
int gap;
for (gap = 4; gap > 0; gap /= 2) {
for (int j = gap; j < arr.length; j++) {
for (int i = j; i > gap - 1; i -= gap) {
if (arr[i] < arr[i - 1]) {
swap(arr, i, i - gap);
}
}
}
}
}
*大家可以看到我在循环里加入了一段小代码
System.out.println("gap=" + gap + " j=" + j + " i=" + i);
print(arr);
System.out.println("\n");
来体现这个程序的计算步骤,也就是第一步的“步骤分解”,这样更便于直观地理解算法的运行流程,大家可以自己尝试一下,运行结果:
gap=4 j=4 i=4
5 8 7 6 9 4 3 2 1
gap=4 j=5 i=5
5 4 7 6 9 8 3 2 1
gap=4 j=6 i=6
5 4 3 6 9 8 7 2 1
gap=4 j=7 i=7
5 4 3 2 9 8 7 6 1
gap=4 j=8 i=8
5 4 3 2 1 8 7 6 9
gap=4 j=8 i=4
1 4 3 2 5 8 7 6 9
gap=2 j=2 i=2
1 4 3 2 5 8 7 6 9
gap=2 j=3 i=3
1 2 3 4 5 8 7 6 9
gap=2 j=4 i=4
1 2 3 4 5 8 7 6 9
gap=2 j=4 i=2
1 2 3 4 5 8 7 6 9
gap=2 j=5 i=5
1 2 3 4 5 8 7 6 9
gap=2 j=5 i=3
1 2 3 4 5 8 7 6 9
gap=2 j=6 i=6
1 2 3 4 5 8 7 6 9
gap=2 j=6 i=4
1 2 3 4 5 8 7 6 9
gap=2 j=6 i=2
1 2 3 4 5 8 7 6 9
gap=2 j=7 i=7
1 2 3 4 5 6 7 8 9
gap=2 j=7 i=5
1 2 3 4 5 6 7 8 9
gap=2 j=7 i=3
1 2 3 4 5 6 7 8 9
gap=2 j=8 i=8
1 2 3 4 5 6 7 8 9
gap=2 j=8 i=6
1 2 3 4 5 6 7 8 9
gap=2 j=8 i=4
1 2 3 4 5 6 7 8 9
gap=2 j=8 i=2
1 2 3 4 5 6 7 8 9
gap=1 j=1 i=1
1 2 3 4 5 6 7 8 9
gap=1 j=2 i=2
1 2 3 4 5 6 7 8 9
gap=1 j=2 i=1
1 2 3 4 5 6 7 8 9
gap=1 j=3 i=3
1 2 3 4 5 6 7 8 9
gap=1 j=3 i=2
1 2 3 4 5 6 7 8 9
gap=1 j=3 i=1
1 2 3 4 5 6 7 8 9
gap=1 j=4 i=4
1 2 3 4 5 6 7 8 9
gap=1 j=4 i=3
1 2 3 4 5 6 7 8 9
gap=1 j=4 i=2
1 2 3 4 5 6 7 8 9
gap=1 j=4 i=1
1 2 3 4 5 6 7 8 9
gap=1 j=5 i=5
1 2 3 4 5 6 7 8 9
gap=1 j=5 i=4
1 2 3 4 5 6 7 8 9
gap=1 j=5 i=3
1 2 3 4 5 6 7 8 9
gap=1 j=5 i=2
1 2 3 4 5 6 7 8 9
gap=1 j=5 i=1
1 2 3 4 5 6 7 8 9
gap=1 j=6 i=6
1 2 3 4 5 6 7 8 9
gap=1 j=6 i=5
1 2 3 4 5 6 7 8 9
gap=1 j=6 i=4
1 2 3 4 5 6 7 8 9
gap=1 j=6 i=3
1 2 3 4 5 6 7 8 9
gap=1 j=6 i=2
1 2 3 4 5 6 7 8 9
gap=1 j=6 i=1
1 2 3 4 5 6 7 8 9
gap=1 j=7 i=7
1 2 3 4 5 6 7 8 9
gap=1 j=7 i=6
1 2 3 4 5 6 7 8 9
gap=1 j=7 i=5
1 2 3 4 5 6 7 8 9
gap=1 j=7 i=4
1 2 3 4 5 6 7 8 9
gap=1 j=7 i=3
1 2 3 4 5 6 7 8 9
gap=1 j=7 i=2
1 2 3 4 5 6 7 8 9
gap=1 j=7 i=1
1 2 3 4 5 6 7 8 9
gap=1 j=8 i=8
1 2 3 4 5 6 7 8 9
gap=1 j=8 i=7
1 2 3 4 5 6 7 8 9
gap=1 j=8 i=6
1 2 3 4 5 6 7 8 9
gap=1 j=8 i=5
1 2 3 4 5 6 7 8 9
gap=1 j=8 i=4
1 2 3 4 5 6 7 8 9
gap=1 j=8 i=3
1 2 3 4 5 6 7 8 9
gap=1 j=8 i=2
1 2 3 4 5 6 7 8 9
gap=1 j=8 i=1
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
Process finished with exit code 0
上面我们直接把gap定为4,是出于最简单的一种考虑——把数组一分为2,由于数组长度为9,则9/2=4。然而这样的想法是最优的吗?很遗憾不是的,这里介绍一个Knuth序列(Donald.Knuth发明),
h=3*h+1
以此来界定间隔序列会效率更高(别问我为啥,我也没搞懂,先记住)h尽可能取大,直到要到数组长度的三分之一了则停止,作为初始的gap值,采用Knuth序列的sort方法改成如下:
static void sort(int[] arr) {
int h = 1;
while (h <= arr.length / 3) {
h = h * 3 + 1;
} //得出h=4,真巧
for (int gap = h; gap > 0; gap = (gap - 1) / 3) {
for (int j = gap; j < arr.length; j++) {
for (int i = j; i > gap - 1; i -= gap) {
if (arr[i] < arr[i - gap]) {
swap(arr, i, i - gap);
System.out.println("gap=" + gap + " j=" + j + " i=" + i);
print(arr);
System.out.println("\n");
} else {
System.out.println("gap=" + gap + " j=" + j + " i=" + i);
print(arr);
System.out.println("\n");
}
}
}
}
}