浅谈八大排序之希尔排序

其实希尔排序就是对插入排序的一个升级版,要想理解希尔排序,深入理解插入排序是重点,
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。希尔排序是记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

我们分割待排序记录的目的是减少待排序记录的个数,并使整个序列向基本有序发展。而如上面这样分完组后,就各自排序的方法达不到我们的要求。因此,我们需要采取跳跃分割的策略:将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。
在这里插入图片描述
其实我们之前所说的插入排序,就是希尔排序步长为1的情况,而希尔排序,需要进行多次步长进行插入排序,所以我们可以先写一个方法,此方法有两个参数,一个为数组引用,一个为步长
好我们先来分析下,就以上面的数组为例,
第一次分组: gap = 10 / 2 = 5
0 1 2 3 4 5 6 7 8 9

9 1 2 5 7 4 8 6 3 5
9 4
1 8
2 6
5 3
7 5
第一次分组 如上图所示
第一组 9 4
第二组 1 8
类推
我们要做的就是对每一组进行插入排序 但是不能改变该数在数组中的位置

private static void sortInit(int[]) arr, int grp){
	
	// 从第一组的第二个数开始 到数组末尾结束
	for(int i = grp; i < arr.length; i++){
		int temp = arr[i];
		// 
		int j = i - grp;
		while(j >= 0 && arr[j] > temp){
		// 当进入循环时 说明 在此数前方有比他大的数  就将比他大的数 后移
		arr[j + grp] = arr[ j ];
		j  -= grp;   // 这里是因为我们要在自己的组里进行插入排序
		}
		// 退出循环时找到位置
		arr[j + grp] = temp;
}
}

到此我们针对每一组的插入排序就已经写好了 下面想想大家都已经直到怎么写了

public static void shellSort(int[] arr) {
		// 不妨每一趟除以2
       for(int grp = arr.length / 2; grp >= 1; grp /= 2){
       			//进行插入排序
				sortInit(arr, grp);
}

}

算法分析:
时间复杂度:

最好情况:由于希尔排序的好坏和步长gap的选择有很多关系,因此,目前还没有得出最好的步长如何选择(现在有些比较好的选择了,但不确定是否是最好的)。所以,不知道最好的情况下的算法时间复杂度。
最坏情况下:O(N*logN),最坏的情况下和平均情况下差不多。
已知的最好步长序列是由Sedgewick提出的(1, 5, 19, 41, 109,…)。
这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。”用这样步长序列的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快,但是在涉及大量数据时希尔排序还是比快速排序慢。

空间复杂度
由直接插入排序算法可知,我们在排序过程中,需要一个临时变量存储要插入的值,所以空间复杂度为1。

算法稳定性
希尔排序中相等数据可能会交换位置,所以希尔排序是不稳定的算法。

猜你喜欢

转载自blog.csdn.net/weixin_44112559/article/details/100020116