插入排序(c语言实现)

1. 直接插入排序

情景:拿到一个数key,要把他插入到一个已经有序的数组。我们从这个有序数组的最后一个位置的数开始比较如果比key大就把这个数往后挪一个位置,如果小于等于key就把key插入到这个数的后面。

实际中我们玩扑克牌时,就用了两种排序一种是直接插入排序,一种是选择排序。

单趟

我们可以从单趟排起,在一组有效数据中,如下图,我们要插入5,我们把数据一个一个往后挪,最终到它相应的位置上

void InsertSort(int* a, int n)
{
	// 把end+1位置数据插入到[0, end]的有序区间内
	int end;
	int x = a[end - 1];
	while (end >= 0)
	{
		if (a[end] > x)
		{
			a[end + 1] = a[end];
			--end;
		}
		else
		{
			break;
		}
	}
	// 不论是end小于0终止还是break终止的循环,最后都是把x放到end+1的位置
	a[end + 1] = tmp;
}

我们从end开始比较,end为10大于5,把10往后挪,end--,再和5比,这样一直比下去,直到找到它的位置。

多趟

搞清楚单趟是怎么回事后,我们就知道了多趟是怎么样的,如下图

void InsertSort(int* a, int n)
{
	assert(a);
	for (int i = 0; i < n - 1; ++i)
	{
		int end = i;
		int x = a[end + 1];
		while (end >= 0)
		{
			if (a[end] > x)
			{
				a[end + 1] = a[end];
				--end;
			}

			else
				break;
		}

		a[end + 1] = x;
	}
	
}

直接插入排序的特性

  1. 元素已经有序或接近有序时,直接插入排序的时间效率越高。
  2. 时间复杂度:O(N^2)。 最好情况(有序):O(N),最坏情况(逆序):O(N^2))
  3. 空间复杂度:O(1)
  4. 稳定性:稳定

希尔排序

基本思想:

对于直接插入排序,有序或者接近有序时最快,时间复杂度差不多是O(N)。希尔排序就是在直接插入排序之前对原数组进行预排序,使得原数组接近有序。直接插入排序相当于gap=1

初始排序越逆,希尔排序效果越好

预排序

设间隔 gap ,就是每间隔gap个数的数组成一组,一共有gap组。当gap=1时就是直接插入排序。

void ShellSort(int* a, int n)
{
	// 对间隔为gap的其中一组数据进行直接插入排序
	int gap;
	int end;
	int x = a[end + gap];
	while (end >= 0)
	{
		if (a[end] > x)
		{
			a[end + gap] = a[end];
			end-=gap;
		}
		else
		{
			break;
		}
	}
	a[end + gap] = x;
}

完成一组排序

通过外套一个for循环,完成1组的排序,其中i代表end的值,从0开始,循环条件为i小于n-gap就可以完成所有组的排序。注意这里不是严格按照一组一组的排,而是混在一起,但最终完成的效果还是一样的。

void ShellSort(int* a, int n)
{
	// 1、对gap为间隔的每一组进行排序
	int gap;
	for (int i = 0; i < n - gap; +=gap)
	{
		int end=i;
		int x = a[end + gap];
		// 2、单次预排序
		while (end >= 0)
		{
			if (a[end] > x)
			{
				a[end + gap] = a[end];
				end -= gap;
			}
			else
			{
				break;
			}
		}
		a[end + gap] = x;
	}
}

完成排序

void ShellSort(int* a, int n)
{
	// 1、对gap为间隔的每一组进行排序
	int gap = 3;
	for (int j = 0; j < gap; j++)
	{
		for (int i = 0; i < n - gap; i += gap)
		{
			int end = i;
			int x = a[end + gap];
			// 2、单次预排序
			while (end >= 0)
			{
				if (a[end] > x)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = x;
		}
	}
}

优化排序

对于以上代码,我们可以进行优化

// 希尔排序
void ShellSort(int* a, int n)
{
	int gap=n;
	// 1、预排序,直到gap为1,就是直接插入排序
	while (gap > 1)
	{
		gap = gap / 3 + 1;// 最后的+1保证gap最后可以为1
		// 2、对gap为间隔的每一组进行排序
		for (int i = 0; i < n - gap; ++i)
		{
			int end = i;
			int x = a[end + gap];
			// 3、单次预排序
			while (end >= 0)
			{
				if (a[end] > tmp)
				{
					a[end + gap] = a[end];
					end -= gap;
				}
				else
				{
					break;
				}
			}
			a[end + gap] = x;
		}
	}
}

希尔排序的特性

希尔排序是对直接插入排序的优化。当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。整体而言,可以达到优化的效果。
时间复杂度:O(N^1.3 — N^2)
空间复杂度:O(1)
稳定性:不稳定,因为依据gap分成不同组,相同数据的相对位置可能被打乱

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值