希尔排序

算法思想

  1. 先取一个正整数d1<n,把所有序号相隔d1的数组元素放一组,组内进行直接插入排序;
  2. 然后取d2<d1,重复上述分组和排序操作;
  3. 直至di=1,即所有记录放进一个组中排序为止。

如下图,该列表有九个项。如果我们使用三的增量,有三个子列表,每个子列表可以通过插入排序进行排序:

完成这些排序后,我们得到如下图 所示的列表。虽然这个列表没有完全排序,但发生了很有趣的事情。 通过排序子列表,我们已将项目移动到更接近他们实际所属的位置:

接下来使用增量为 1 的插入排序; 换句话说,标准插入排序。注意,通过执行之前的子列表排序,我们减少了将列表置于其最终顺序所需的移位操作的总数。对于这种情况,我们只需要四次移位完成该过程:

动画演示:

实现

C++

void shellSort(vector<int> &array)  
{
	int len = array.size();

	int i, j, gap;   // gap为步长,每次减为原来的一半。
	for (gap = len / 2; gap > 0; gap /= 2)
	{
		// 共gap个组,对每一组都执行直接插入排序
		for (i = 0; i < gap; i++)
		{
			for (j = i + gap; j < len; j += gap)
			{
				// 如果array[j] < array[j-gap],则寻找array[j]位置,并将后面数据的位置都后移。
				if (array[j] < array[j - gap])
				{
					int tmp = array[j];
					int k = j - gap;
					while (k >= 0 && array[k] > tmp)
					{
						array[k + gap] = array[k];
						k -= gap;
					}
					array[k + gap] = tmp;
				}
			}
		}
	}
}

python

def shellSort(alist):
    sublistcount = len(alist)//2

    while sublistcount > 0:
    #一次将分段的起始元素开始输入到分段插入排序中
      for startposition in range(sublistcount):
        gapInsertionSort(alist,startposition,sublistcount)

      sublistcount = sublistcount // 2

#本质上实现的是插入排序
def gapInsertionSort(alist,start,gap):
    for i in range(start+gap,len(alist),gap):

        currentvalue = alist[i]
        position = i

        while position>=gap and alist[position-gap]>currentvalue:
            alist[position]=alist[position-gap]
            position = position-gap

        alist[position]=currentvalue

总结

希尔排序的增量:
希尔排序的增量数列可以任取,需要的唯一条件是最后一个一定为1(因为要保证按1有序)。

稳定性:
Shell排序是一个多次插入的过程,在一次插入中我们能确保不移动相同元素的顺序,但在多次的插入中,相同元素完全有可能在不同的插入轮次被移动,最后稳定性被破坏,因此,Shell排序不是一个稳定的算法。

适用场景:
Shell排序虽然快,但是毕竟是插入排序,其数量级并没有后起之秀--快速排序快。在大量数据面前,Shell排序不是一个好的算法。但是,中小型规模的数据完全可以使用它。

复杂度:
\(O(N * \log N)\)

posted @ 2019-04-09 09:51  youngliu91  阅读(180)  评论(0编辑  收藏  举报