最近在学习希尔排序,记录一下学习的笔记
/**
* 希尔排序
* 算法步骤:
* 1. 选择一个递增序列(这里使用的递增序列为 1/2 * (3^k-1) -> 1,4,13,40,121,...)
* 2. 按递增序列K,对序列进行K趟排序
* 3. 每趟排序,根据对应的增量ti,将待间隔为ti的元素构成子数组。
* 在子数组中将每个元素交换到比它大的元素之前去,最终子数组内均有序
* 4. 按递增序列递减增量ti,重复2.3. 当增量为1时,整个数组有序
*
*/
function shellSort(arr) {
arr = arr.slice(0);
var len = arr.length;
var gap = 1;
var temp = null;
while (gap < Math.floor(len / 3)) {
gap = 3 * gap + 1;
}
while (gap >= 1) {
// 间隔为gap的元素构成子数组,确保子数组是有序的
for (var i = gap; i < len; i++) {
/*
* j >= gap && arr[j] < arr[j - gap] 关于这句话我理解了好久
* 为什么用这个作为循环呢?
* 它的意思是:
* 首先,数组以及变成多个间隔为gap的子数组了。
* 然后从元素arr[gap]开始与前面间隔gap的元素arr[0]比较,
* 如果arr[gap]<arr[0]就要交换
* 进行完这一过程,arr[gap]肯定是比arr[0]大了,
* 而且现在arr[gap]前的数组只有arr[0],所以开始比较arr[gap+1];
* 一直到arr[2*gap]之前,arr[j]都最多只需要交换一次(与arr[j-gap])就可以了
* 到了arr[2*gap],
* a/ 这时如果arr[2*gap] 大于 arr[gap],
* 而在前面以及确保过arr[gap]和arr[0]是有序的了,
* 因此arr[2*gap]所在的子数组在它及前面元素已经是有序的了,
* 进而可以结束循环,进行下一个元素arr[2gap + 1];
* b/ 如果arr[2*gap] 小于 arr[gap]
* 这时arr[2*gap]之前的子数组是有序的,
* 因此至少是说arr[2*gap]小于前面的一个最大值,
* 为了保证arr[2gap]及之前的有序,需要进行交换
* 首先arr[2gap]的值A 与间隔gap的arr[gap]的值B交换,
* 此时值A在arr[gap]的位置上了,
* 这时显然还需要继续比较arr[gap]与arr[0]才能确保,
* arr[2gap]、arr[gap]、arr[0]三者是有序的
* 因此
* 循环会判断j从2gap变成gap(值A所在位置索引)后前面还有没有元素(j >= gap)
* 这时是有的(arr[0])于是循环会继续判断arr[j]是不是比前一个元素大
* 这里其实是和前面元素的最大值做比较,因此只要大于等于就可以直接结束循环了
* 小于就进行交换,至此arr[2gap]所在子数组,在它及它之前的元素均已有序,
* 继续下面的循环,直到在间隔为gap的所有子数组有序为止
*/
for (var j = i; j >= gap && arr[j] < arr[j - gap]; j -= gap) {
temp = arr[j];
arr[j] = arr[j - gap];
arr[j - gap] = temp;
}
}
// 按照递增序列递减间隔
gap = Math.floor(gap / 3);
}
return arr;
}