排序——希尔排序

希尔排序

希尔排序步骤

希尔排序的核心还是插入排序 ,但是把插入排序分成两部分,1.预排序2.插入排序 。先对原数组进行预排序,使数组接近有序(让更大的数字和更小的数字更快的分配到两边),然后再对已经接近有序的数组进行插入排序,这样就会快很多。

时间复杂度约为:O(N^1.3);空间复杂度O(1); 由于希尔排序有分预排序,所以有可能对相同大小的数字会改变他们的相对位置,是不稳定排序

预排序:把原数组分组,然后对不同的分组进行插入排序,例如下面这串逆序的数字,我们对其分组分为3组,先进行预排序,让数组接近有序。

不断地进行预排序,直到我们取的gap=1(gap表示同组数据之间的间距),就是直接插入排序,这样我们就很好的优化了直接插入排序的时间复杂度,希尔排序就是直接插入排序的进阶版。

我们用gap表示一组的间隔,这样总共就有gap组(上述案例gap=3)。当gap过大时,较大的大的数和较小的数就能更快地跳到两边,但是相对的,一次gap排完后数组也更不接近有序;当gap过小时,就约等于直接插入排序,遇到数据过多时,希尔排序就失去了原本的意义。所以一般先给gap赋值n(总数)再取gap=gap/2或者gap=gap/3+1(gap除2的最后一次gap一定是1,gap除3的最后一次可能会是0,为了让最后一次gap一定为1,所以再+1),这样就刚好达到了一个平衡。(gap=1就是直接插入排序)

****总结:****希尔排序核心还是是直接插入排序,差别在于直接插入排序就是gap=1,而希尔排序是gap从n/2或者n/3+1开始不断地趋于1,最终gap=1进行一次直接插入排序就完成了。但希尔排序内部还有不同写法,下述为希尔排序的两种写法。

希尔排序写法

单组排序:

排完一组再重新返回起点,再后移一位排下一组,总共排gap组。

多组并排:

每次排序完后往后移动一位,排下一组,当向后移gap位后又重新排第一组。即i=0时是一组,i++后又是一组,间隔是gap,(i=0和i+=gap是同一组)

计算希尔排序时间复杂度

我们先假设n(总数)很大,外面的时间复杂度就为log3n,接着计算内部预排序的时间复杂度。由于n很大,那么第一次gap=n/3+1也会很大,我们忽略后面的+1,这样相当于有n/3组,每组有3个元素,当逆序情况时,每组要移动1+2=3次,那么累计的次数就是n/3*3=n。

然后gap在除3,gap=gap/3+1,我们在忽略+1,就得到了gap=n/9,相当于分成了n/9组,每组要移动(1+2+3+...+8)=36次,再当当前数据还是逆序,那么累计次数为n/9*36=4n,看起来是变大了的,但实际上因为我们移动过了原逆序数据,第二次移动时肯定不为完全逆序,次数肯定会减少。

这样一直减小gap,知道gap=1时,就是直接插入排序,这时由于我们的数据经过上述预排序后已经变得非常接近有序了,所以累计次数就相当于n(走一遍数组,调一小部分),总时间复杂度就为上述累计次数相加,时间复杂度大约为O(N^1.3)

希尔排序代码

单组排序:

cpp 复制代码
//希尔排序
void ShellSort(int *arr, int n)
{
	int gap = n;
	//重新上来时gap=1就说明上次排序是直接插入排序
	while (gap > 1)
	{
		//gao有两种取法:如下
		//gap = gap / 2;
		gap = gap / 3 + 1;

        //单组排序
		//一组一组排序,排完一组再排下一组,总共排gap组
		for (int j = 0; j < gap; j++)
		{
			//每组下标i+=gap,向后移gap位
			for (int i = 0; i < n - gap; i += gap)
			{
				//直接插入
				int end = i;
				int tmp = arr[end + gap];
				while (end >= 0)
				{
					if (tmp < arr[end])
					{
						arr[end + gap] = arr[end];
						end -= gap;
					}
					else
						break;
				}
				arr[end + gap] = tmp;
			}
		}
	}
}

多组并排:

cpp 复制代码
//希尔排序
void ShellSort(int *arr, int n)
{
	int gap = n;
	//重新上来时gap=1就说明上次排序是直接插入排序
	while (gap > 1)
	{
		//gao有两种取法:如下
		//gap = gap / 2;
		gap = gap / 3 + 1;

		//多组并排,i=0时是一组,i++后又是一组,间隔是gap,(i=0和i+=gap是同一组)
		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			int tmp = arr[end + gap];
			while (end >= 0)
			{
				if (tmp < arr[end])
				{
					arr[end + gap] = arr[end];
					end -= gap;
				}
				else
					break;
			}
			arr[end + gap] = tmp;
		}
	}
}
相关推荐
九圣残炎26 分钟前
【从零开始的LeetCode-算法】1456. 定长子串中元音的最大数目
java·算法·leetcode
lulu_gh_yu31 分钟前
数据结构之排序补充
c语言·开发语言·数据结构·c++·学习·算法·排序算法
丫头,冲鸭!!!1 小时前
B树(B-Tree)和B+树(B+ Tree)
笔记·算法
Re.不晚1 小时前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
为什么这亚子2 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
2 小时前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习
~yY…s<#>2 小时前
【刷题17】最小栈、栈的压入弹出、逆波兰表达式
c语言·数据结构·c++·算法·leetcode
幸运超级加倍~3 小时前
软件设计师-上午题-16 算法(4-5分)
笔记·算法
yannan201903133 小时前
【算法】(Python)动态规划
python·算法·动态规划
埃菲尔铁塔_CV算法3 小时前
人工智能图像算法:开启视觉新时代的钥匙
人工智能·算法