常用的排序算法之希尔排序(Shell Sort)
希尔排序(Shell Sort)
起源
希尔排序(Shell Sort)是Donald Shell于1959年提出的一种基于插入排序的算法。它是对直接插入排序算法的一种更高效的改进版本,也称为“缩小增量排序”。
定义
希尔排序是插入排序的一种更高效的改进版本,也称为缩小增量排序。希尔排序是非稳定排序算法。该方法的基本思想是:先将整个待排序的记录序列分割成为若干子序列(由相隔某个“增量”的记录组成)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
引伸义
希尔排序的引伸义在于其“增量”的选取。增量序列的选取对希尔排序的效率有很大的影响。不同的增量序列可能带来不同的时间复杂度。例如,Hibbard提出了另一个增量序列{1, 3, 7, ..., 2^k-1},称为Hibbard增量,在最好情况下时间复杂度为O(n^(3/2))。还有更复杂的增量序列,如Sedgewick增量,旨在进一步减少希尔排序的比较次数。
优缺点
优点:
- 希尔排序是基于插入排序的,因此它保持了插入排序的稳定性(当增量为1时)和简单性。
- 相比插入排序,希尔排序在数据量大且无序的情况下效率更高。
缺点:
- 希尔排序的时间复杂度与增量序列的选取有关,最坏情况下时间复杂度仍为O(n^2)。
- 希尔排序不是稳定的排序算法,因为不同的增量可能导致相等元素的相对顺序发生变化。
使用场景
希尔排序适用于中等规模的数据排序,对于大规模数据,更高效的排序算法(如快速排序、归并排序等)可能更为合适。此外,希尔排序由于其简单性和稳定性(当增量为1时),也常用于教学示例。
使用数据一步步举例
假设有一个数组[9, 8, 3, 7, 5, 6, 4, 1]
,我们使用希尔排序(增量序列为[4, 2, 1])来对其进行升序排序:
- 增量为4:将数组分成四个子序列[9, 5],[8, 4],[3, 1],[7, 6],分别进行插入排序。
- [5, 9],[4, 8],[1, 3],[6, 7]
- 增量为2:将数组分成两个子序列[5, 4, 1, 6]和[9, 8, 3, 7],分别进行插入排序。
- [4, 1, 5, 6],[7, 3, 8, 9]
- 增量为1:此时整个数组已经基本有序,进行一次插入排序即可。
- [1, 3, 4, 5, 6, 7, 8, 9]
Java示例代码
public class ShellSort { public static void main(String[] args) { int[] array = {9, 8, 3, 7, 5, 6, 4, 1}; shellSort(array); System.out.println(Arrays.toString(array)); } public static void shellSort(int[] array) { int n = array.length; int gap = n / 2; // 初始增量 // 动态定义间隔序列 while (gap > 0) { for (int i = gap; i < n; i++) { int temp = array[i]; int j; for (j = i; j >= gap && array[j - gap] > temp; j -= gap) { array[j] = array[j - gap]; } array[j] = temp; } gap /= 2; // 减小增量 } } }
运行上述代码,将输出排序后的数组[1, 3, 4, 5, 6, 7, 8, 9]
。