希尔排序
希尔排序
希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n^2)的第一批算法之一
基本思想
希尔排序的基本思想是:现将待排序的数组分成多个待排序的子序列,使得每个子序列的元素较少,然后对各个子序列分别进行插入排序,待到整个待排序的序列基本有序的时候,最后在对所有的元素进行一次插入排序。
希尔排序的具体步骤如下:
- 选择一个步长序列t1,t2,t3,···,tk, 满足tm>tn(m < n), tk = 1
- 按照步长序列个数,对待排序序列进行k趟排序
- 每趟排序根据对应的步长,将待排序序列分割成若干个子序列,分别对这些子序列进行插入排序
我们选择的步长是一个缩小增量序列,通常情况下选择的步长为gap=length/2
,缩小增量继续以gap=gap/2
的方式,直到增量最后变成1,即{n/2,(n/2)/2...1}。希尔排序的增量序列的选择是一个数学难题,通常选取这个常用的增量序列。下图示例了希尔排序的过程:
代码实现
/**
* ShellSort
*/
import java.util.Arrays;
public class ShellSort {
/**
* 希尔排序 针对有序序列在插入时采用交换法
*/
public static void shellSort(int[] array) {
int len = array.length;
if (len <= 0) {
return;
}
//初始化步长
int gap = len>>1;
//循环控制缩小步长
for (; gap >= 1; gap >>= 1) {
//从第gap个元素,逐个对其所在组的元素进行插入排序
for (int i = gap; i < len; i++) {
int j = i;
while (j-gap>=0 && array[j] < array[j-gap]) {
swap(array, j, j-gap);
j -= gap;
}
}
}
}
/**
* 希尔排序 针对有序序列在插入时采用移动法
*/
public static void shellSortS(int[] array) {
int len = array.length;
if (len <= 0) {
return;
}
int gap = len>>1;
for (; gap >= 1; gap >>= 1) {
for (int i = gap; i < len; i++) {
int j = i;
int tmp = array[j];
while (j-gap>=0 && array[j] < array[j-gap]) {
array[j] = array[j-gap];
j -= gap;
}
array[j] = tmp;
}
}
}
public static void swap(int[] array, int pos1, int pos2){
int tmp = array[pos1];
array[pos1] = array[pos2];
array[pos2] = tmp;
}
public static void main(String []args){
int []arr ={1,4,2,7,9,8,3,6};
shellSort(arr);
System.out.println(Arrays.toString(arr));
int []arr1 ={1,4,2,7,9,8,3,6};
shellSortS(arr1);
System.out.println(Arrays.toString(arr1));
}
}
复杂度及稳定性
希尔排序时间复杂度
希尔排序的时间复杂度与增量(即,步长gap)的选取有关。例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为O(N²),而Hibbard增量{1, 3, ..., 2^k-1}的希尔排序的时间复杂度为O(N3/2)。
希尔排序稳定性
希尔排序是不稳定的算法,它满足稳定算法的定义。对于相同的两个数,可能由于分在不同的组中而导致它们的顺序发生变化。
算法稳定性 -- 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!