希尔排序
排序步骤
1、分组,以任意长度进行分组(这个长度我们称作增量gap);通常以总长度的一半这个数为依据进行分组,每间隔 gap 个数即为一组
2、组内排序;组内使用插入排序法进行排序
3、重新设置间隔分组;此次间隔数一般为原来的间隔数 gap 的基础上减半
4、组内排序
.......
重复如上操作,最终,间隔数会减半到1,实现整体的插入排序,整体序列变为有序
什么不直接使用插入排序法?
希尔排序实际上是对插入排序的一种优化。插入排序在面对大规模的杂乱的数据时,需要进行多次交换,较为复杂;而希尔排序将大规模的数据减半,极大缩小了需要排序数据的规模,同时在最终的整体的数据进行排序时,整体数据近乎有序,此时进行插入排序的效率好
解析说明
希尔排序的效率取决于增量的选取,时间复杂度并不是一个定值
1) 最好情况:序列是正序排列,在这种情况下,需要进行的比较操作需(n-1)次。后移赋值操作为0次。即O(n)
2) 最坏情况:O(nlog2n)
3) 平均时间复杂度:O(nlog2n)
开始时,gap 取值较大,子序列中的元素较少,排序速度快,克服了直接插入排序的缺点
其次,gap值逐渐变小后,虽然子序列的元素逐渐变多,但大多元素已基本有序,所以继承了直接插入排序的优点,能以近线性的速度排好序
因此,希尔排序的时间复杂度相对于O(n²)好一些
最优的空间复杂度为开始元素已排序,则空间复杂度为 0;
最差的空间复杂度为开始元素为逆排序,则空间复杂度为 O(N);
平均的空间复杂度为O(1)
希尔排序代码
import org.junit.Test;
public class ShellSort {
@Test
public void test(){
int[] arr = new int[]{12, 15, 4, 5, 8, 35};
shellSprt(arr);
for (int i : arr) {
System.out.print(i + " ");
}
}
public static int[] shellSprt(int[] arr){
int gap = arr.length /2;//一开始时的间距数
int temp = 0;//插入排序的临时数
while(gap > 0){//最后一遍排序的gap值为1
for (int i = gap; i < arr.length; i++) {//每组的第一个数被认为是有序。因此从第二组数开始进行插入排序
temp = arr[i];//当前需要插入的数
for (int j = i; j >= gap; j -= gap) {//每组之间的每个数之间都相差gap个数
if(temp < arr[j - gap]){//插入排序向前比较
arr[j] = arr[j - gap];
}else{
break;//找到插入的位置则退出
}
arr[j - gap] =temp;//需要插入的位置
}
}
gap /= 2;//gap不断减半直到其值为1
}
return arr;
}
}
引用
【算法】排序算法之希尔排序 - 知乎 (zhihu.com)
排序:希尔排序(算法) - 简书 (jianshu.com)