算法思想
希尔排序是对插入排序的优化。
先把待排序数组进行间隔分组,对每一个分组进行插入排序,相同间隔大小的分组排序完成后,缩小间隔,再分组,再插入排序......直到间隔为1,即最原始的插入排序。
在分隔逐步细化的过程中,大数会集中到后半部分,小数会集中到前半部分,虽然它们此时不一定有序。
代码实现
二分间隔分组法
public static void main(String[] args) {
// 构造一个无序的数组
int[] arr = {2, 5, 6, 8, 9, 7, 4, 1, 3, 0, 11, 15, 13, 18, 14, 16, 19, 17, 12, 10};
sort(arr);
System.out.println("最终结果:" + Arrays.toString(arr));
}
/**
* 由于希尔排序是插入排序的改进,则先把插入排序的逻辑拿过来,并做修改
*/
public static void insertionSort(int[] arr, int h) {
/* “新数”的起始位置应该是间隔的位置,其中h是间隔(即第一个数索引位置+h=第二个数索引位置)
i++:是下一组的第一个“新数”索引位置
*/
for (int i = h; i < arr.length; i++) {
/* 内层循环就是已经排好序的数组,j是“新数”的下标
j -= h:是指“新数”的前一个数(即已排好序的最后一个数),循环则是依次向前比较
j >= h:交换体中的下标j-h最小就是0(即,h-h),所以应该是j>=h,且防止数组下标越界
*/
for (int j = i; j >= h; j -= h) {
// 拿这个“新数”和已经排好序的数组依次比较,交换,最终找到“新数”应该在的位置
if (arr[j - h] > arr[j]) {
int tmp = arr[j - h];
arr[j - h] = arr[j];
arr[j] = tmp;
}
}
// 打印每个“新数”插入后的结果
System.out.println(Arrays.toString(arr));
}
}
public static void sort(int[] arr) {
for (int h = arr.length/2; h > 0; h /= 2) {
insertionSort(arr, h);
}
}
knuth间隔分组法
public static void sort(int[] arr) {
int init = 1;
/* 确定最初的间隔,即最粗的间隔,在for循环中逐步细化;
init <= arr.length / 3是为了防止第一个间隔大小就比数组长度还大(越界)
*/
while (init <= arr.length / 3) {
// knuth间隔分组的公式
init = 3 * init + 1;
}
// 本例中,最开始的间隔是13(此时,就一个数在那里排,因为13+13就超出数组长度了),然后再依次缩小,直到间隔为1
for (int h = init; h > 0; h = (h - 1) / 3) {
insertionSort(arr, h);
}
}
复杂度和稳定性
时间复杂度(不同的文章可能不一样)
- 最优时间复杂度:O(n)
- 最坏时间复杂度:O(
)
- 平均时间复杂度:O(
)
空间复杂度:O(1)
稳定性:稳定