希尔排序
1. 算法概述
是直接插入排序算法
的一种更高效的改进版本,希尔排序是非稳定
排序算法。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序
。
2. 算法原理
希尔排序的思想是使数组中任意间隔为h(步长)的元素是有序的。这样的数组也称为h有序数组,也即一个数组是h个互相独立的有序数组交叉编制在一起组成的数组。如图所示为一个h有序数组包含了4个有序子数组。
上图中,假设步长为4,则使 L-M-P-T,E-H-S-S,E-L-O-X,A-E-L-R为有序,然后将这4个数组交叉编制在一起组成一个大的数组:L-E-E-A-N-H-L-E-P-S-O-L-T-S-X-R,
排序过程:
- 先取一个步长h1<arr.length(取值正整数),把所有序号相隔h1的数组元素放一组,组内进行直接插入排序
- 然后在取一个步长h2<h1,重复上述分组和排序操作;
- 直至hi=1,即所有记录放进一个组中排序为止
- 实际就是arr[0]和arr[h]比较,arr[1]和arr[h+1]比较。。。每比较完一轮后,就缩小h的值
- 该方法实质上是一种分组插入方法
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。
3. 动图演示
下面动图以步长为arr.length/2为例,进行演示
4. 代码实现
@Test
public void sort() {
int[] arr = new int[]{49, 38, 65, 97, 76, 13, 27, 49, 78, 34, 12, 64, 1};
//设置步长,一般为数组(起始数组length >>> 1,分割数组length >>> 1 >>> 1...)长度一半
int gap = arr.length >>> 1;
//int gap = arr.length / x(设定步长);
//根据步长,分割数组
//for (; gap > 0; gap /= gap) {
for (; gap > 0; gap >>>= 1) {
//设置 第2个数组 起始索引 left = gap; 之后步长跨越为 left = left + gap * 循环次数
for (int start = gap; start < arr.length; start += gap) {
int temp = arr[start];
//start - gap 为前一个 步长数组 相对位置的 元素
int before = start - gap;
boolean swap = false;
//如果 前数据 > 后数据,则交换两者位置。依次,向前推,直至before <0 即表示该相对位置的元素已经有序
for (; before >= 0 && arr[before] > temp; before = before - gap) {
arr[before + gap] = arr[before];
swap = true;
}
if (swap) {
//最终 before + gap 位置会被空出,将临时元素填充进去
arr[before + gap] = temp;
}
}
}
log.info("排序后:{}", JSON.toJSONString(arr));
}