目录
1、描述
2、特点
3、代码实现
4、输出结果
5、使用场景
解释1:希尔数组的思想是是数组中任意间隔为h的元素都是有序的。这样的数组被称为 h 有序数组。换 句话说,一个 h 有序数组就是 h 个互相独立的有序数组编织在一起组成的一个数组。
解释2:在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。
解释3:再通俗的说,希尔排序是特殊的插入排序,他也是假定前面n-1一个元素是有序的,然后将第n个元素插入到前面有序的列表中,不同的是它不是和n-1个元素依次比较,然后插入,它是根据h这个增量,和n-h、n-2h比较的。
就比如int[] a={1,5,2,3,4,9,7,6,8},我们定h=4;
我们会拿 a[4] 与a[0] 比较,交换成有序,形成了一个子有序数组a1={1,4}
然后我们拿a[5]与a[1]比较,交换成有序,形成第二个子有序数组a2={5,9}
然后我们拿a[6]与a[2]比较,交换成有序,形成第三个子有序数组a3={2,7}
......................a4={3,6}......a5={4,8}
第一轮比完了,这时候的数组交换成 a={1,5,2,3,4,9,7,6,8} 然后我们需要将h的值减小,可以让子有序数组的的间隔变小,我们令h=h/2=2;
我们 我们会拿 a[2] 与a[0] 比较,交换成有序,形成了一个子有序数组a1={1,2}
我们 我们会拿 a[3] 与a[1] 比较,交换成有序,形成了一个子有序数组a2={3,5}
我们 我们会拿 a[4] 与a[2] 、a[0]比较,交换成有序,形成了一个子有序数组a3={1,2,4}
.................a4={3,5,9}............a5={1,2,4,7}........a6={3,5,6,9}.........
我们 我们会拿 a[8] 与a[6] 、a[4]、a[2]、a[0]比较,交换成有序,形成了一个子有序数组a7={1,2,4,7,8}
第二轮的数组结果a={1,3,2,5,4,6,7,9,8}
最后一轮我们将h设为h=h/2=1,再依次比较依次 这时候就和我们的插入排序一样了,将它们的依次比较得到想要的序列。
public class Shell {
private static int count = 0;
private static int count1 = 0;
private static int countX = 0;
private static int countX1 = 0;
private static int countInserrion = 0;
private static int countInserrion1 = 0;
/**
* 希尔排序 h=3x+1
*/
private static void sort(int[] a) {
int N = a.length;
int h = 1;
// 3x+1 hment sequence: 1, 4, 13, 40, 121, 364, 1093, ...
while (h < N / 3)
h = 3 * h + 1;
while (h >= 1) {
System.out.println("h=" + h);
for (int i = h; i < N; i++) {
for (int j = i; j >= h; j -= h) {
count++;
if (a[j] < a[j - h]) {
count1++;
exch(a, j, j - h);
} else {
break;
}
}
}
h /= 3;
}
}
/**
* 希尔排序 h= N/2
*/
public static void shell_sort(int array[]) {
int temp = 0;
int N = array.length;
int h = N;
do {
h = h / 2;
System.out.println("hX=" + h);
for (int k = 0; k < h; k++) { // 根据增量分为若干子序列
for (int i = k + h; i < N; i += h) {
for (int j = i; j > k; j -= h) {
countX++;
if (array[j] < array[j - h]) {
countX1++;
temp = array[j - h];
array[j - h] = array[j];
array[j] = temp;
} else {
break;
}
}
}
}
} while (h != 1);
}
/**
* 插入排序
*/
public static void sortInserrion(int[] a) {
for (int i = 1; i < a.length; i++) {
for (int j = i; j > 0; j--) {
countInserrion++;
if (a[j] < a[j - 1]) {
countInserrion1++;
exch(a, j, j - 1);
} else {
break;
}
}
}
}
private static void exch(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
private static void show(int[] a) {
System.out.println("\n");
for (int item : a) {
System.out.print(item + ",");
}
}
public static void main(String[] args) {
int[] a = { 55, 43, 23, 12, 13, 11, 7, 8, 88, 6, 4, 2, 3, 1, 9, 8, 7, 11, 56, 45, 22, 23,
45, 66 };
sort(a);
show(a);
int[] b = { 55, 43, 23, 12, 13, 11, 7, 8, 88, 6, 4, 2, 3, 1, 9, 8, 7, 11, 56, 45, 22, 23,
45, 66 };
shell_sort(b);
show(b);
int[] c = { 55, 43, 23, 12, 13, 11, 7, 8, 88, 6, 4, 2, 3, 1, 9, 8, 7, 11, 56, 45, 22, 23,
45, 66 };
sortInserrion(c);
show(c);
System.out.println("\ncount=" + count);
System.out.println("count1=" + count1);
System.out.println("countX=" + countX);
System.out.println("countX1=" + countX1);
System.out.println("countInserrion=" + countInserrion);
System.out.println("countInserrion1=" + countInserrion1);
}
}
从结果我们明显看出希尔排序在循环和比较的次数上明显优于插入排序,按照(3x+1)设定h比其他的设定h值,循环次数要少。
//希尔排序1
h=13
h=4
h=1
1,2,3,4,6,7,7,8,8,9,11,11,12,13,22,23,23,43,45,45,55,56,66,88
//希尔排序x
hX=12
hX=6
hX=3
hX=1
1,2,3,4,6,7,7,8,8,9,11,11,12,13,22,23,23,43,45,45,55,56,66,88,
//插入排序
1,2,3,4,6,7,7,8,8,9,11,11,12,13,22,23,23,43,45,45,55,56,66,88,
//希尔排序1
count=87
count1=43
//希尔排序1
countX=97
countX1=34
//插入排序
countInserrion=144
countInserrion1=130
有经验的程序员有时会选择希尔排序,因为对于中等大小的数组它的运行时间是可以接受的。 它的代码量很小,且不需要使用额外的内存空间。虽然有更加高效的算法,但 除了对于很大的 N,它们可能只会比希尔排序快两倍(可能还达不到),而且更复杂。如果你需要 解决一个排序问题而又没有系统排序函数可用(例如直接接触硬件或是运行于嵌入式系统中的代 码),可以先用希尔排序,然后再考虑是否值得将它替换为更加复杂的排序算法。
本文内容部分来之于《算法》,本文目的主要为了学习笔记。