希尔排序
概述
希尔排序是插入排序的改进版,也称缩小增量排序。其核心思想是将原始数组按增量分成多个子序列,对每组进行插入排序,随后逐步缩小增量直至为1,最终完成整体排序。
常见的增量序列间隔选择方式
-
Shell原始序列 :1 2 ... <math xmlns="http://www.w3.org/1998/Math/MathML"> n / 4 n/4 </math>n/4 <math xmlns="http://www.w3.org/1998/Math/MathML"> n / 2 n/2 </math>n/2 (n为原始数组长度)
-
Hibbard序列 :1 3 7 ... <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 k − 1 2^k-1 </math>2k−1
-
Knuth序列 :1 4 13 ... <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 3 k − 1 ) / 2 (3^k-1)/2 </math>(3k−1)/2
最佳增量序列的的选择仍然是一个研究方向。
JavaScript实现
Shell序列
javascript
function shellSort(list) {
let n = list.length;
let gap = n >> 1;
while (gap) {
// 分别处理以i开头,间隔gap的子序列
for (let i = 0; i < gap; i++) {
for (let j = i + gap; j < n; j += gap) {
let k = j - gap;
while (k >= 0 && list[k] > list[k + gap]) {
swap(list, k, k + gap);
k -= gap;
}
}
}
// 缩小gap
gap = gap >> 1;
}
return list
}
for (let i = 0; i < 100; i++) {
let list = generateList();
console.log('origin list', list);
let a = sort(clone(list)), b = shellSort(clone(list));
console.log('sorte', a);
console.log('shell', b);
for (let j = 0; j < list.length; j++) if (a[j] !== b[j]) console.log('error');
console.log('success')
}
// ---------------help----------------------
function clone(list) {
return list.slice(0);
}
function swap(list, i, j) {
var temp = list[i];
list[i] = list[j];
list[j] = temp;
}
function generateList() {
let length = 10 + Math.ceil(Math.random() * 10);
let list = [];
while (length) {
list.push(Math.ceil(Math.random() * 100));
length--;
}
return list;
}
function sort(list) {
list.sort((a, b) => a - b);
return list;
}
Hibbard序列
javascript
function shellSortByHibbard(list) {
let n = list.length, K = Math.floor(Math.log2(n + 1));
console.log('长度', n, 'K=', K);
while (K) {
let gap = Math.pow(2, K) - 1;
// 分别处理以i开头,间隔gap的子序列
for (let i = 0; i < gap; i++) {
for (let j = i + gap; j < n; j += gap) {
let k = j - gap;
while (k >= 0 && list[k] > list[k + gap]) {
swap(list, k, k + gap);
k -= gap;
}
}
}
K--;
}
return list;
}
Knuth序列
javascript
function shellSortByKnuth(list) {
let n = list.length, K = Math.floor(Math.log(2 * n + 1) / Math.log(3));
console.log('长度', n, 'K=', K);
while (K) {
let gap = (Math.pow(3, K) - 1) >> 1;
// 分别处理以i开头,间隔gap的子序列
for (let i = 0; i < gap; i++) {
for (let j = i + gap; j < n; j += gap) {
let k = j - gap;
while (k >= 0 && list[k] > list[k + gap]) {
swap(list, k, k + gap);
k -= gap;
}
}
}
K--;
}
return list;
}