希爾排序(Shell sort)
[toc]
1959年由唐納德·希爾(Donald Shell)提出
1.思路
- 希爾排序把序列看作是一個矩陣,分成 ?? 列,逐列進行排序
- ?? 從某個整數(shù)逐漸減為1
- 當 ?? 為1時,整個序列將完全有序
- 因此,希爾排序也被稱為遞減增量排序(Diminishing Increment Sort)
矩陣的列數(shù)取決于步長序列(step sequence)
- 比如,如果步長序列為{1,5,19,41,109,...},就代表依次分成109列、41列、19列、5列、1列進行排序
- 不同的步長序列,執(zhí)行效率也不同
1.1舉例
希爾本人給出的步長序列是 ,比如 ?? 為16時,步長序列是{1, 2, 4, 8}
例如
原數(shù)組為
a1 = [16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]
- 第一次分為8列,則分成2個數(shù)組
[16,15,14,13,12,11,10,9]
[ 8, 7, 6, 5, 4, 3, 2,1]
逐列進行比較數(shù)組變成
[ 8, 7, 6, 5, 4, 3, 2,1]
[16,15,14,13,12,11,10,9]
然后合并成一個數(shù)組
a2=[ 8, 7, 6, 5, 4, 3, 2,1,16,15,14,13,12,11,10,9] - 再將數(shù)組a2分為4列,則分為4個數(shù)組
[ 8, 7, 6, 5]
[ 4, 3, 2, 1]
[16,15,14,13]
[12,11,10, 9]
逐列進行比較
[ 4, 3, 2, 1]
[ 8, 7, 6, 5]
[12,11,10, 9]
[16,15,14,13]
然后合并成一個數(shù)組
a3=[ 4, 3, 2, 1, 8, 7, 6, 5,12,11,10, 9,16,15,14,13] - 再將數(shù)組a3分為2列,則分成的那個8個數(shù)組
[ 4, 3]
[ 2, 1]
[ 8, 7]
[ 6, 5]
[12,11]
[10, 9]
[16,15]
[14,13]
逐列進行比較
[ 2, 1]
[ 4, 3]
[ 6, 5]
[ 8, 7]
[10, 9]
[12,11]
[14,13]
[16,15]
然后合并成一個數(shù)組
a4=[ 2, 1, 4, 3, 6, 5, 8, 7,10, 9,12,11,14,13,16,15] - 在將數(shù)組a4分為1列,則分成16個數(shù)組,
[ 2]
[ 1]
[ 4]
……
[16]
[15]
逐列進行比較,然后合并成一個數(shù)組
a5=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]完成
每次進行拆分列數(shù)后,逆數(shù)對的個數(shù)減少
因此希爾排序底層一般使用插入排序對每一列進行排序,也很多資料認為希爾排序是插入排序的改進版
1.2代碼思路
1.2.1 編寫函數(shù)返回步長序列
這里給希爾給的步長序列希爾本人給出的步長序列是 ,比如 ?? 為16時,步長序列是{1, 2, 4, 8}
///
/// Author: liyanjun
/// description:
///采用希爾給出的步長序列
List<int> _createShellStepSequence() {
List<int> stepSequence = [];
int step = list.length;
if (step == 0) return [];
if (step == 1) return [1];
while (step > 1) {
step >>= 1;
stepSequence.add(step);
}
return stepSequence;
}
1.2.2 編寫函數(shù)對第col列進行插入排序
- 假設元素在第 col 列、第 row 行,步長(總列數(shù))是 step
- 那么這個元素在數(shù)組中的索引是 col + row * step
- 所以插入排序for循環(huán)中每次增加的數(shù)字為step,而不再是1
- 因此原來插入排序相應的加1減1的操作都變成了 加step減step
///
/// Author: liyanjun
/// description: 分成step列進行排序
_sout(int step) {
//col 代表列,從
for (var col = 0; col < step; col++) {
//第col列進行排序 進行插入排序 這里的代碼是插入排序的移動代碼的優(yōu)化版本
// 原來相應的加1減1的操作都變成了 加step減step
for (var begin = col+step; begin < list.length; begin +=step) {
int cur = begin;
T v = list[cur];
while (cur > col && cmpElement(v, list[cur - step]) < 0) {
list[cur] = list[cur - step];
cur -= step;
}
list[cur] = v;
}
}
}
1.2.3 遍歷步長序列
sort() {
List<int> stepSequence = _createShellStepSequence();
for (var step in stepSequence) {
_sout(step);
}
}
2.代碼
/// Date: 2020-11-09 00:01:18
/// FilePath: /algorithm/sort/shell_sort.dart
/// Description: 希爾排序
///
class ShellSort<T extends Comparable<T>> extends Sort<T> {
@override
sort() {
List<int> stepSequence = _createShellStepSequence();
for (var step in stepSequence) {
_sout(step);
}
}
///
/// Author: liyanjun
/// description: 分成step列進行排序
_sout(int step) {
//col 代表列,從
for (var col = 0; col < step; col++) {
//第col列進行排序 進行插入排序 這里的代碼是插入排序的移動代碼的優(yōu)化版本
// 原來相應的加1減1的操作都變成了 加step減step
for (var begin = col+step; begin < list.length; begin +=step) {
int cur = begin;
T v = list[cur];
while (cur > col && cmpElement(v, list[cur - step]) < 0) {
list[cur] = list[cur - step];
cur -= step;
}
list[cur] = v;
}
}
}
///
/// Author: liyanjun
/// description:
///采用希爾給出的步長序列
List<int> _createShellStepSequence() {
List<int> stepSequence = [];
int step = list.length;
if (step == 0) return [];
if (step == 1) return [1];
while (step > 1) {
step >>= 1;
stepSequence.add(step);
}
return stepSequence;
}
驗證
main(List<String> args) {
// List<int> list = IntergerTools.random(10000, 1, 20000); //生成10000個數(shù),最小是1,最大是20000
List<int> list = IntergerTools.random(10, 1, 20); //生成10000個數(shù),最小是1,最大是20000
List<Sort> sortList = [
// HeapSort<num>(),
// SelectSort<num>(),
// BubbleSort2<num>(),
// BubbleSort1<num>(),
// BubbleSort<num>(),
// InsertSort<num>(),
// InsertSort1<num>(),
// InsertSort2<num>(),
// MergeSort<num>(),
// QuickSort<num>(),
ShellSort<num>(),
];
testSorts(list, sortList);
}
void testSorts(List<int> list, List<Sort> sorts) {
print('排序前 :$list');
for (Sort sort in sorts) {
List<int> newList = List.from(list);
sort.setList(newList);
sort.sortPrint();
Asserts.test(IntergerTools.isAscOrder(sort.list));
print('排序后 :${sort.list}');
}
sorts.sort(); //比較
for (Sort sort in sorts) {
print(sort);
}
}
結果
排序前 :[2, 18, 8, 20, 10, 9, 10, 12, 7, 9]
排序后 :[2, 7, 8, 9, 9, 10, 10, 12, 18, 20]
【ShellSort<num>】
耗時:0.002s (2ms) 比較:27 交換:0
-----------------
3.時間復雜度
空間復雜度為O(1),屬于不穩(wěn)定排序
最好情況是步長序列只有1,且序列幾乎有序,時間復雜度為 O(n)
希爾給出的步長序列 ,的最壞情況時間復雜度是
目前已知的最好的步長序列,最壞情況時間復雜度是 ,1986年由Robert Sedgewick提出
步長算法思路:
- 如果k是偶數(shù),則步長序列為
- 如果k是奇數(shù),則步長序列為
///
/// Author: liyanjun
/// description: 科學家發(fā)現(xiàn)最快的步長序列 ,最壞情況時間復雜度是 $O(n^{4/3})$
///
List<int> _sedgewickStepSequence() {
List<int> stepSequence = [];
int k = 0, step = 0;
while (true) {
if (k % 2 == 0) {
int powResult = pow(2, k >> 1); //求2^{k/2}的結果
step = 1 + 9 * (powResult * powResult - powResult);
} else {
int pow1 = pow(2, (k - 1) >> 1); //求2^{k-1/2}的結果
int pow2 = pow(2, (k + 1) >> 1); //求2^{k-1/2}的結果
step = 1 + 8 * pow1 * pow2 - 6 * pow2;
}
if (step >= list.length) break;
stepSequence.insert(0, step);
k++;
}
return stepSequence;
}