希尔排序 Shell Sort
接下面的几节介绍几个不常用的排序算法.
本节介绍的希尔排序 (shell sort) 是插入排序的 (insertion sort) 的变体.
插入排序的一个问题是, 将元素 k
移动到左侧排序好的数组中的位置时, 通常还要移动元素 k
左侧的元素,
而移动元素的成本比较高. 希尔排序对这个过程做了优化, 以减少移动元素的次数.
希尔排序将数组拆解成由 h
个元素组成的小数组, 依次降低h间隔的值, 直到其为1, 这样就减少了元素交换的次数.
希尔排序的步骤
- 初始化间隔值
h = len / 3
- 使用插入排序法, 将
arr[h..]
与arr[..h]
间的元素进行排序, 使用插入排序法, 但两个待比较的元素的间隔是h
, 而不是默认的1
, 这一步很重要, 它有助于减少元素的移动次数 - 减少间隔值,
h /= 3
, 重复上面的步骤, 直到最后一个循环h = 1
这里的 h
值是由大到小变化的, 就是说, 每次移动的步长是h, 就是为了减少元素被移动的次数.
当 h = 1 时, 整个序列就完成排序了.
希尔排序的实现
#![allow(unused)] fn main() { /// Shell sort is a simple extension to insertion sort that allows exchanging /// elements that far apart. /// /// It produces partially sorted array (h-sorted array). pub fn shell_sort<T>(arr: &mut [T]) where T: PartialOrd, { const FACTOR: usize = 3; let len = arr.len(); // 计算第一个 gap 的值, 大概是 len/3 let mut h = 1; while h < len / FACTOR { h = FACTOR * h + 1; } while h >= 1 { // 使用插入排序, 将 `arr[0..h]` 排序好 for i in h..len { let mut j = i; while j >= h && arr[j - h] > arr[j] { arr.swap(j - h, j); j -= h; } } h /= FACTOR; } } }
希尔排序的特点
- 最差情况下的时间复杂度
O(n^2)
, 空间复杂度是O(1)
- 最好情竞下的时间复杂度是
Ω(n log(n))
- 比插入排序快
- 与插入排序不同的时, 希尔排序适合大中型的数组, 对于任意顺序的数组也有效