”插入排序“&&”选择排序“

我们学过冒泡排序,堆排序等等。(回顾一下:排升序,建大堆;排降序,建小堆。)

排序:所谓排序,就是使⼀串记录,按照其中的某个或某些关键字的⼤⼩,递增或递减的排列起来的
操作。

在生活中也会遇到很多排序:购物筛选排序(按照价格、综合、销量、距离等排序)、百度热搜(根据热搜程度)、院校排名等等。

在这里插入图片描述

插入排序

1. 直接插入排序(O(n^2))

基本思想:将(待排序的记录)按照(关键码值的大小)逐个插入到(已经排好序的有序队列)中。直到所有的记录插入完为止,得到一个新的有序序列。

先将arr[end]指向:(有序队列的最后一个数据),然后将(需要和有序数据进行比较的数据:即arr[end+1])暂时存储在tmp中,接下来将arr[end]和tmp里的数据进行比较。
在这里插入图片描述
注意在一次循环中,tmp的值是不变的(除非end++,才会使tmp的值改变(tmp=arr[end]))

举例1:

【假设想要排升序】
在这里插入图片描述

  • 现在arr[end]的值大于tmp,应该排在后面,所以现在将end的值放在end+1的位置,这个时候会发现end+1位置的值被覆盖了,所以我们需要提前将它的值存储在tmp中(这就是将值存储在tmp中的原因)。接下来需要将tmp的值放在end的位置(这个是arr[end]的前面没有其他值的情况,我们可以直接将tmp的值赋给arr[end])

在交换值之后,将end++,tmp的值为arr[end+1] (它不用修改,因为end已经修改了)
在这里插入图片描述

  • 如果arr[end]<tmp,并不需要交换呢?(比如5和9)那就直接end++,紧接着tmp的值也变啦。

  • 紧接着又是将9和6交换,按照之前的方法(将arr[end]的值赋给arr[end+1],然后将tmp的值赋给arr[end]
    在这里插入图片描述

  • 为什么我们还需要将交换之后的arr[end]的值和这个序列前面的值比较呢?------防止它比前面已经排好序的值小

接下来按照前面的方法,再将9和2进行比较

举例2:

在这里插入图片描述

直插排序的"代码"

我们需要一个循环来控制end++,往后走;
还需要一个循环来控制:当把arr[end]赋值给arr[end+1]之后,需要将end- -,将前面的数值和tmp继续比较。

在这里插入代码片`void InsertSort(int* arr, int n)
{
   
	for (int i = 0; i < n - 1; i++)  //为什么是i<n-1?
	{
   
		int end = i;
		int tmp = arr[end + 1];
		while (end >= 0)
		{
   
			if (arr[end] > tmp)
			{
   
				arr[end + 1] = arr[end];
				end--;
			}
			else{
   
				break;
			}
		}
		arr[end + 1] = tmp;
	}
}`

在这里插入图片描述

  • 为什么是i<n-1呢?
    因为tmp存储的是arr[end+1],我们要确保它是最后一个不会越界,即end+1<n,又因为end=i,所以i+1<n,所以i<n-1

直插排序的“时间复杂度”

直接插入排序的时间复杂度是O(n^2)

不过O(n^2)是最差的情况。
在这里插入图片描述

最差:想将降序----->升序 O(n^2)
最好:升序------->升序 O(n)

2. 希尔排序(O(n^1.3))

希尔排序就是对直接插入排序的一种优化。想要改善降序---->升序的时间复杂度。

希尔排序分为两步:1.预排序 2.直接插入排序

我们可以将很长的一段数组序列变为n段较短的序列,然后对每段“单独”进行直接插入排序(这个叫做预排序)。预排结束之后,小的数据大部分都在前半部分了(只是不按顺序),接着我们再对整体的数组(已经趋于有序的数组)序列进行一次直插排序,这样不仅完成了排序,还优化了时间复杂度。

方法一
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值