希尔排序
一、原理
☆思想:插入排序在记录数较少或序列基本有序时效率高,为了满足这些条件,希尔排序对插入排序做了改进;对序列按一定间隔分组,在每组内进行插入排序,不断缩小间隔直到1,即对整个序列进行插入排序,最终得到有序序列;
☆过程:以递增为例,用数组表示,长度为n,整个数组为无序集合;间隔选择方法很多,不一而论,一般为二分、三分等;以初始间隔二分为例,每组最多三个元素,基本是两个元素,如有11个元素,初始间隔为11/2=5,可分为五组,分别为{1,6,11}、{2,7}、{3,8}、{4,9}、{5,10},对每组进行插入排序;缩小间隔为5/2=2,可分为两组,分别为{1,3,5,7,9,11}、{2,4,6,8,10},对每组进行插入排序;缩小间隔为2/2=1,对应整个序列,进行插入排序,排序结束;
二、实现代码
方法一:JavaScript 代码常规实现,层次清晰,容易理解;对分组进行插入排序时,每次把本组所有元素排序完成,再进行下一组排序;
function ShellSort(arr) { var len = arr.length; var step = Math.floor(len / 2); var curr, preIndex; while (step >= 1) { //固定步长,所有分组首元素 for (var i = 0; i < step; i++) { //对某分组进行插入排序 for (var j = i + step; j < len; j += step) { curr = arr[j]; for (var preIndex = j - step; curr < arr[preIndex] && preIndex >= 0;preIndex -= step) { arr[preIndex + step] = arr[preIndex]; } arr[preIndex + step] = curr; } } //步长折半递减 step = Math.floor(step / 2); console.log('' + arr) } }
方法二:JavaScript 代码简化实现,少一层循环,代码简洁;进行插入排序时,从第一组第二个元素开始,每次插入一个元素,具体过程为插入第一组第二个元素、插入第二组第二个元素、插入第三组第二个元素、…直到最后一个元素,交替插入不同组元素;
function ShellSort(arr) { var length = arr.length, gap = Math.floor(length / 2), temp; while (gap > 0) { //从每组第二个元素开始插入排序 for (var i = gap; i < length; i++) { temp = arr[i]; for (var j = i - gap; j >= 0 && arr[j] > temp; j -= gap) { arr[j + gap] = arr[j]; } arr[j + gap] = temp; } console.log(gap + ':' + arr); gap = Math.floor(gap / 2); } return arr; }
三、优化
无;
四、复杂度
名称 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
平均 | 最坏 | 最优 | |||
希尔排序 | O(n^1.3) | O(n²) | O(nlogn) | O(1) |
X |
最差情况,数组反序,间隔n/2,需要进行n/2 *1次比较,间隔n/4,需要进行n/4 *(1+2+3)次比较,间隔n/8,需要进行n/8 *(1+2+...+7)次比较,…间隔1,需要进行1 *(1+2+3+……+(n-1))=n(n-1)/2次比较,即每个间隔需要(div-1)*n/2次比较,div为2^1、2^2、2^3…,共需要n/2+3n/2+7n/2+...+(n-1)n/2次比较;
最优情况,数组正序,间隔n/2,需要进行n/2次比较,间隔n/4,需要进行n/4 *3次比较,间隔n/8,需要进行n/8 *7次比较,…间隔1,需要进行(n-1)次比较,即每个间隔需要(div-1)*n/div次比较,div为2^1、2^2、2^3…,共需要n/2+3n/4+7n/8+...+(n-1)≈nlogn次比较;
本排序需要申请一个额外空间;
使用数组表示,相同元素顺序可能变化,本排序不稳定。