概述
希尔排序(Shell Sort)是一种基于插入排序的排序算法,通过将待排序的元素分组进行插入排序,逐步减小分组的间隔,最终使整个序列有序。
希尔排序的基本思想是将待排序的序列划分为若干个较小的子序列,对这些子序列进行插入排序。初始时,选择一个较大的间隔值(称为增量),按照该增量将序列分成多个子序列,并对每个子序列进行插入排序。然后逐渐缩小增量,重复进行分组和插入排序,直到增量为1,完成最后一次插入排序,整个序列就变成有序的。
具体的步骤如下:
- 选择一个增量值(通常为数组长度的一半),并设定为gap。
- 根据增量gap,将待排序序列分成若干个子序列,每个子序列相邻元素之间的间隔为gap。
- 对每个子序列进行插入排序,即从第gap个元素开始,按照插入排序的方式将元素插入到前面已排序的子序列中。
- 缩小增量gap,重复步骤3,直至gap为1,完成最后一次插入排序。
时间空间复杂度分析
时间复杂度: 希尔排序的时间复杂度是比较复杂的,由于增量序列的选择不同,最坏情况下的时间复杂度可以达到O(n^2),但在一般情况下,希尔排序的平均时间复杂度为O(n log n)。
究其原因,希尔排序通过将待排序序列划分为若干个子序列进行插入排序,每次排序时的间隔逐渐缩小。这样可以使得在初始阶段,每个子序列中的元素之间相隔较远,通过一次插入排序可以快速将较小的元素移动到正确的位置。随着间隔的缩小,每次排序时元素之间的距离逐渐变小,最终在增量为1时完成排序。
空间复杂度: 希尔排序是一种原地排序算法,它只需要常数级别的额外空间来存储临时变量,所以其空间复杂度为O(1)。
基于java实现
代码语言:javascript复制package day1120;
public class ShellSort {
public static void shellSort(int[] arr) {
int n = arr.length;
// 初始增量为数组长度的一半,然后逐渐缩小增量
for (int gap = n / 2; gap > 0; gap /= 2) {
// 对每个子序列进行插入排序
for (int i = gap; i < n; i ) {
int temp = arr[i];
int j = i;
// 插入排序
while (j >= gap && arr[j - gap] > temp) {
arr[j] = arr[j - gap];
j -= gap;
}
arr[j] = temp;
}
}
}
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
System.out.println("原始数组:");
for (int num : arr) {
System.out.print(num " ");
}
shellSort(arr);
System.out.println("n排序后数组:");
for (int num : arr) {
System.out.print(num " ");
}
}
}
- 执行
shellSort
方法时,将传入一个整型数组arr
作为参数。 - 取数组的长度
n
,然后使用初始增量gap
,该增量的初始值为数组长度的一半。我们会逐渐缩小gap
的值,直到它为1。这样做是为了分组进行插入排序,初始时每个分组的元素相隔较远,可以更快地将较小的元素移动到正确的位置。 - 使用一个外层循环来控制
gap
的缩小过程。在每次循环中,我们使用一个内层循环对每个子序列进行插入排序。 - 内层循环从
gap
开始,依次遍历数组中的元素。对于每个元素,我们将其保存在临时变量temp
中,并使用j
记录其位置。 - 内层循环的内部,我们使用另一个循环实现插入排序。我们通过比较
j
与gap
的差值来确定是否需要交换元素的位置。如果前一个分组的元素大于当前元素,则将前一个分组的元素移到当前位置,并将j
减去gap
,以便在下一次循环中继续比较。 - 将保存在临时变量
temp
中的值放置在正确的位置上,完成一次插入排序。 - 外层循环会重复进行,直到
gap
的值为1,此时进行最后一次插入排序,将整个数组排序完成。