希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,希尔排序是直接插入排序的升级版本,也称为缩小增量排序,同时该算法是冲破O(n^2)的第一批算法之一。
直接插入排序算法的思想:
把n个待排序的元素看成一个有序表,一个无序表,开始的时候,有序表只包含n-1个元素,排序过程中每次从无序表中取第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将他插入到有序表的合适位置
希尔排序的基本思想是:
先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。
希尔排序的排序效率和选择步长序列有直接关系,从length逐步减半,这还不算最快的希尔,还有其他别的步长序列会更快,这里就不再说明,具体可以去各大网站查找
由于直接插入排序在元素基本有序的情况下效率很高,因此希尔排序在时间效率上比前两种方法有较大提高。
下面我写了交换式希尔排序和移位式希尔排序
交换式希尔排序内部使用了 冒泡排序
移位式希尔排序内部使用了 直接插入排序
public class ShellSort{
public static void main(String[] args){
int[] arr = {1, 21, 12, 2, 32, 41, 2, 3, 231, 5, 87};
shellSort(arr);
}
//交换法的希尔排序
//先推导3轮 最后整合最终的表达式
public static void shellSort(int[] arr){
int temp = 0;
//希尔排序第一轮排序
//因为第一轮排序是将10个数据分成5组
for(int i = 5; i < arr.length; i++){
//下面是在分好的数组中进行直接插入排序
for(int j = i - 5;j >= 0;j -= 5){
if(arr[j]>arr[j + 5]){
temp = arr[j];
arr[j] = arr[j + 5];
arr[j + 5] = arr[j];
}
}
}
//希尔排序第二轮排序
//因为第二轮排序是将10个数据分成5 / 2组 = 2组
for(int i = 2; i < arr.length; i++){
for(int j = i -2;j >= 0; j-=2){
if(arr[j] > arr[j+2]){
temp = arr[j];
arr[j] = arr[j + 2];
arr[j + 2] = temp;
}
}
}
//希尔排序第三轮排序
for(int i = 1;i < arr.length;i++){
for(int j = i - 1;j >= 0;j-=1){
if(arr[j] > arr[j + 1]){
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
推出最终的交换式希尔排序算法
//以此类推
int temp = 0;
int count = 0;
boolean flag = false;
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]){
flag = true;
temp = arr[j];
arr[j] = arr[j + gap];
arr[j + gap] = temp;
}
//这里对插入算法进行优化
//当判定第一个插入比较的时候,如果没有发成交换
//则说明都是有序的 不需要再排序了
if(!flag){
break;
}else{
flag = false;
}
}
}
}
但我们发现交换式希尔排序的效率并不高
于是我们决定使用移位式希尔排序(内部利用了插入排序)
int count = 0;
//增量gap 并逐步的缩小增量
for(int gap = arr.length/2;gap>0;gap/=2){
//从第gap个元素,逐个对其所在组进行直接插入排序
//其实就是把交换法中的冒泡排序变成了直接插入排序
for(int i = gap;i<arr.length;i++){
int j = i;//待插入位置
int temp = arr[j];
//如果arr[j] >= arr[j - gap] 本轮就没必要进行插入排序
//比如 {1, 2, 4} 2 < 4 又2和1一定是排好的 所以本轮不需要排序
if(arr[j]<arr[j-gap]) {
while(j - gap >= 0 && temp < arr[j-gap]) {
//移动
arr[j] = arr[j - gap];//发现待插入的比插入的小 就把插入的值后移gap位
j -= gap;//插入坐标前移gap位
}
//退出while循环表明 已经找到插入位置
//即temp >= arr[j - gap]
//将temp放在j位置上即可
arr[j] = temp;
}
}
count++;
}
}