直接插入排序&希尔排序

1.直接插入排序

1.思想

把待排序的记录,按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。在现实生活中,在我们打牌时,每当我们拿了一张牌,然后我们就用会用插入排序将其放到正确的位置。

2.实现思路

现在有一个数组:“4,3,2,1,6,5,9,8,7,0”,要求使用插入排序使数组有序,首先假设这个数组第一个元素有序,然后分别拿后面的元素进行插入排序,如果后面的元素比首元素大,那么就放在首元素的后面,如果后面的元素小于首元素,我们就将首元素向后挪,然后再插入。

假设首元素'4'有序,用‘3’插入如下:

'3'插入完后,那么'3, 4'就有序了,我们再拿’2‘进行插入,’2‘比’4‘小,’4‘往后挪,‘2’比‘3’小,‘3’往后挪,然后插入‘2’

一次类推,直到最后最后一个元素插入新序列。

3.代码

#include <stdio.h>
void InsertSort(int arr[], int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = arr[i + 1];
		while (end >= 0)
		{
			if (arr[end] > tmp)
			{
				arr[end + 1] = arr[end];
				end--;
			}
			else
			{
				break;
			}
		}
		arr[end + 1] = tmp;
	}
	
}
int main()
{
	int arr[] = { 4, 3, 2, 1, 6, 5, 9, 8, 7, 0 };
	int n = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	InsertSort(arr, n);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

4.代码分析

在插入排序中,首先我们假设第一个元素有序,然后定义tmp变量,用于存放有序序列的下一个元素,end变量表示有序数列的末尾位置,然后用tmp和arr[end]比较,如果tmp<arr[end],我们就把arr[end]往后挪一位,也就是挪到了tmp开始的位置,即使挪完后tmp的值被覆盖,但是我们在一开始就把tmp位置的值保存到了tmp。挪完后,end--,因为tmp还要继续和找比自己小的数据,如果arr[end]还是大于tmp,那么我们就继续将arr[end]往后挪一位,end--,直到tmp找到比自己小的元素,然后插入到它的后面,或者没有找到比它小的数据,那么tmp就插入到arr[0]位置。当一个元素插入完成后,再插入下一个元素,直到最后的元素插入到数组中。

2.希尔排序

1.思想

希尔排序,也称为递减增量排序算法,是插入排序的一种高效率的改进版本。它通过将待排序的序列分割成若干子序列,分别进行直接插入排序,从而达到整个序列有序的目的。希尔排序的核心在于间隔序列的选择,间隔序列通常是按某种规则递减至1的。

2.实现思路

假设现在有序列:“9,1,2,5,7,4,8,6,3,5”,我们不直接对整个数组进行插入排序,我们定义变量gap = 5,然后将这个序列分为5组,相同颜色为一组,然后对每组进行插入排序。

当gap = 5,对这样的分组进行插入排序,只要有一个元素要调整位置,它在分组序列每移动一次,都相当于在初始序列移动5次,这样可以使较小的元素很快的移动到数组前面部分,使较大的元素较快的移动到数组靠后的位置。对每组插入排序后,gap变为2,再分组,再排序。

gap=2排好后,gap最终回归到1,对数组进行整体插入排序,此时的数据基本趋于有序,直接插入排序非常擅长趋于有序的序列排序。

gap=1,排完后,序列就有序了。

3.代码

#include <stdio.h>
void ShellSort(int arr[], int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap /= 2;
		for (int j = 0; j < gap; j++)
		{
			for (int i = j; i < n - gap; i += gap)
			{
				int end = i;
				int tmp = arr[i + gap];
				while (end >= 0)
				{
					if (arr[end] > tmp)
					{
						arr[end + gap] = arr[end];
						end -= gap;
					}
					else
					{
						break;
					}
				}
				arr[end + gap] = tmp;
			}
		}
	}
}
int main()
{
	int arr[] = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5};
	int n = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	ShellSort(arr, n);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

4.代码分析

1.gap变化分析

gap的取值再分组插入排序中,比较关键,gap第一次可以去n/2,对数组元素进行跨度较大的插入排序,然后每一循环,只要gap大于1都将gap整除2,gap最后一次一定是1,当gap=1时,对数组进行插入排序后,数组就有序了。

2.循环嵌套分析

序号①的for循环:这个for循环是对gap分好的每一个组进行插入排序

序号①循环:

  1. i = j,每次对i初始化为j,循环最开始的i值,表示分好的组中每组的首元素位置。
  2. i < n-gap是循环的终止条件,i<n-gap说明,i最大为n-gap-1,这是为了防止下面tmp=arr[i+gap]赋值时越界
  3. i+=gap,是因为分好的组里面每个元素位置相差gap,当对一个元素插入排序后,对第二个元素进行插入排序,就要让i+gap

序号②循环两种理解方式:

  1. 我们发现,当gap不仅表示,每组中每个元素的间隔,gap还表示,一共会分出多少组,如果gap=3,那么数组会被分为三组。所以一种理解就是:假设gap=3,那么这个循环的意思是,我们要对三个组进行插入排序,这里的j可以控制循环次数。
  2. 第二种理解是,这里的j也是每组首元素的位置,为i的初始话做准备。

3.时间空间复杂度和稳定性

1.直接插入排序

1.时间复杂度:O(n^2)

2.空间复杂度:O(1)

3.稳定性:稳定

2.希尔排序

1.时间复杂度:O(n^1.3)

2.空间复杂度:O(1)

3.稳定性:不稳定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值