希尔排序是插入排序的一个变种。对于大规模乱序数组插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点一点地从数组的一端移动到另一端。希尔排序为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。
先看一段希尔排序的实现的代码:
public static void ShellSort1() {
int[] a = { 49, 38, 65, 97, 76, 13, 27, 49, 78, 34, 12, 64, 1 };
// 希尔排序
int d = a.length;
while (true) {
d = d / 2;
for (int x = 0; x < d; x++) {
for (int i = x + d; i < a.length; i = i + d) {
int temp = a[i];
int j;
for (j = i - d; j >= 0 && a[j] > temp; j = j - d) {
a[j + d] = a[j];
}
a[j + d] = temp;
}
}
if (d == 1) {
break;
}
}
}
这段代码有三层循环,下面我们将其进行分解。
第一步,先是一个基本的插入排序的算法:
public static void InserSort1(int[] a){
for(int i=1;i<a.length;i=i+1){
int temp = a[i];
int j=i-1;
while(j>=0&&temp<a[j]){
a[j+1]=a[j];
j=j-1;
}
a[j+1]=temp;
}
}
将其进行改进,改为可以指定步进长度,也就是把原来所有出现的“1”都改为“d”。
这样就将默认的步进长度,由“依次”改为人为指定长度d。
public static void InserSort1(int[] a,int d){
for(int i=d;i<a.length;i=i+d){
int temp = a[i];
int j=i-d;
while(j>=0&&temp<a[j]){
a[j+d]=a[j];
j=j-d;
}
a[j+d]=temp;
}
}
第二步,确定步进长度d,采用缩减式。每次缩减为原长度的某个比例。可固定为一半,即divid=2。
public static int MakeSetpLength(int d,int divid){
d=d/divid;
return d;
}
第三步,组合。由大到小的采用一系列步进长度d,当d==1时,再进行最后一次插入排序,然后结束。
public static void ShellSort(int[] a){
int d=a.length;
while(true){
int divid=2;//可以改为变化的缩进,如2,3,5,8...这样的缩进方式。
//这种方式需要记录原长度a.length
d=MakeSetpLength(d,divid);
InserSort1(a,d);
if(d==1){
break;
}
}
}
完整代码如下:
package asen.yang;
public class shellSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] a = { 49, 38, 65, 97, 76, 13, 27, 49, 78, 34, 12, 64, 1 };
ShellSort(a);
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
public static void ShellSort0(Comparable[] a) {
int N = a.length;
int h = 1;
while (h < N / 3) {
h = 3 * h + 1;
}
while (h >= 1) {
for (int i = h; i < N; i++) {
for (int j = i; j >= h && a[j].compareTo(a[j - h]) < 0; j = j - h) {
Comparable temp = a[j];
a[j] = a[j - h];
a[j - h] = temp;
}
}
h = h / 3;
}
}
public static void InserSort1(int[] a){
for(int i=1;i<a.length;i++){
int temp = a[i];
int j=i-1;
while(j>=0&&temp<a[j]){
a[j+1]=a[j];
j--;
}
a[j+1]=temp;
}
}
public static void InserSort1(int[] a,int d){
for(int i=d;i<a.length;i=i+d){
int temp = a[i];
int j=i-d;
while(j>=0&&temp<a[j]){
a[j+d]=a[j];
j=j-d;
}
a[j+d]=temp;
}
}
public static int MakeSetpLength(int d,int divid){
d=d/divid;
return d;
}
public static void ShellSort(int[] a){
int d=a.length;
while(true){
int divid=2;//可以改为变化的缩进,如2,3,5,8...这样的缩进方式。这种方式需要记录原长度a.length
d=MakeSetpLength(d,divid);
InserSort1(a,d);
if(d==1){
break;
}
}
}
public static void ShellSort1() {
int[] a = { 49, 38, 65, 97, 76, 13, 27, 49, 78, 34, 12, 64, 1 };
System.out.println("排序之前:");
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
// 希尔排序
int d = a.length;
while (true) {
d = d / 2;
for (int x = 0; x < d; x++) {
for (int i = x + d; i < a.length; i = i + d) {
int temp = a[i];
int j;
for (j = i - d; j >= 0 && a[j] > temp; j = j - d) {
a[j + d] = a[j];
}
a[j + d] = temp;
}
}
if (d == 1) {
break;
}
}
System.out.println();
System.out.println("排序之后:");
for (int i = 0; i < a.length; i++) {
System.out.print(a[i] + " ");
}
}
}
在上面的代码中,有一个ShellSort0的实现,这种方式在某些教材中出现,但是不好理解。有需要的朋友可以参考一下。