希尔排序小记
希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。
思想: 希尔排序通过将比较的全部元素分为几个区域来提升插入排序的性能。这样可以让一个元素可以一次性地朝最终位置前进一大步(为什么一定是最终位置呢?求高手解释)。然后算法再取越来越小的步长进行排序,算法的最后一步就是普通的插入排序,但是到了这步,需排序的数据几乎是已排好的了(此时插入排序较快)。
一个更好的理解方法:
将数组列在一个表中并对列排序(用插入排序)。重复这过程,不过每次用更长的列来进行。最后整个表就只有一列了。将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i += step_size
而不是i++
)。
例如,假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5行的表中来更好地描述算法,这样他们就应该看起来是这样:
13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10
然后我们对每列进行排序:
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45
当我们以单行来读取数据时我们得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].这时10已经移至正确位置了,然后再以3为步长进行排序:10 14 7325 23 1327 94 3339 25 5994 65 8245排序之后变为:10 14 1325 23 3327 25 5939 65 7345 94 8294最后以1步长进行排序(此时就是简单的插入排序了)。关于步长的选择:步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。但是选取不同的步长序列算法的时间复杂度也不同。一般,比较流行的一种选择方法是:起始步长为n/2(n为数组元素个数),然后步长不断取半减小,知道步长为1的排序结束。当然这种步长序列并不是最好的选择(这里我只使用这种序列)。另外,希尔排序的一个重要的性质是:小步长的排序不会影响大步长排序后的有序性。否则,希尔排序算法也就没什么意义了。(这一性质需要证明,但这里只讨论算法的实现,不做证明,大家可以google一下相关的证明)。伪代码:注:文中部分内容摘自维基百科。void ShellSort( ElementType A[], int N ) { int i,j,gap; ElementType tmp; for(gap = N/2; gap > 0; gap /=2) for(i = gap; i < N; i++) //将A[i]到A[N-1]分别插入到以gap为步长所对应数组的正确位置(插入排序) { tmp = A[i]; for(j = i;j >= gap; j -= gap) { if(tmp < A[j-gap]) A[j] = A[j-gap]; //较大的元素后移 else break; } A[j] = tmp; //插入 } }
志之所趋,无远勿届,穷山复海不能限也;志之所向,无坚不摧。