选择排序及其优化-排序算法连载(二)

选择排序,是最经典的排序算法。下面从以下几个方面介绍该算法。

  1. 算法原理[algorithm principle]
  2. 算法实现[algorithm implementation]
  3. 时间复杂度[time complexity]
  4. 空间复杂度[space complexity]
  5. 稳定性[stability]
  6. 算法优化-(双指针)

1.算法原理

  • 在一个序列当中选出最小值,然后和第一个元素交换
  • 在剩下的序列中找最小值,再和第二个元素交换。
  • 直到只剩下一个元素,排序结束。

1.1 图解算法

假设a=[1,3,2,9,7,2],下面对a进行升序排列。
在这里插入图片描述

1.1.1 进行首次循环

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于2>1 不用更新最小值。
到此,首轮排序结束。
代码描述为:

本次循环坐标=0
开始循环:
	如果 当前值 < 最小值 :
			最小值=当前值
			最小值坐标=当前坐标
循环结束
# 本次循环坐标=0,最小值坐标=0 不用交换
如果 最小值坐标 !=  本次循环坐标 :
	交换 本次循环坐标的元素 和 最小值坐标的元素

1.1.2. 开始第二次循环

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
由于2≥2 不用更新最小值,指针向后移动和下一个元素进行比较。
在这里插入图片描述
第二次循环到达尾声,由于最小值坐标不等于本次循环的初始坐标,所以交换两个元素。
在这里插入图片描述
至此第二次循环结束。
代码描述为:

本次循环坐标=1
开始循环:
	如果 当前值 < 最小值 :
			最小值=当前值
			最小值坐标=当前坐标
循环结束
# 本次循环坐标=1,最小值坐标=2 进行交换
如果 最小值坐标 !=  本次循环坐标 :
	交换 本次循环坐标的元素 和 最小值坐标的元素

下面进行第三次循环,
我们这样…如法炮制…
在这里插入图片描述
在这里插入图片描述
排序完成,结果如下所示。

1.2 代码描述

整体代码描述:

外部循环:从0到数组长度-1
	本次循环坐标=当前外部循环坐标
	内部循环:从本次循环 到 数组的长度-1
		如果 当前值 < 最小值 :
				最小值=当前值
				最小值坐标=当前坐标
	内部循环结束
	# 本次循环坐标=1,最小值坐标=2 进行交换
	如果 最小值坐标 !=  本次循环坐标 :
		交换 本次循环坐标的元素 和 最小值坐标的元素
外部循环结束

2.算法实现

使用语言-python

def select_sort(nums):
	for x in range(len(nums)):
		temp,index=nums[x],x#本次循环坐标=当前外部循环坐标
		for j in range(x,len(nums)):
			if nums[j]<temp:#更新最小值和最小值坐标
				temp,index=nums[j],j
		if index != x:#交换 本次循环坐标的元素 和 最小值坐标的元素
			nums[x],nums[index]=nums[index],nums[x]
	print(nums)

3.时间复杂度

首次循环,选取最小值,进行n-1次比较。
下一次 ,选取最小值,进行n-1次比较。

同时,每次循环结束都要进行一次最小值交换。

  • 最坏情况 共进行n-1次交换
  • 最好情况,进行0次交换

设平均值为 1/2*(n-1)
根据通项公式有T
T = n − 1 + n − 2 + n − 3 + . . . + 1 + 1 2 ∗ ( n − 1 ) = n ( n − 1 + 1 ) + ( n − 1 ) 2 = n 2 + n − 1 2 T=n-1+n-2+n-3+...+1+\frac {1}{2}*(n-1)=\frac {n(n-1+1)+(n-1)}{2}=\frac {n^2+n-1}{2} T=n1+n2+n3+...+1+21(n1)=2n(n1+1)+(n1)=2n2+n1
所以时间复杂度为
O ( T ) = O ( 1 2 ( n 2 + n − 1 ) ) ≈ O ( n 2 ) O(T)=O(\frac{1}{2} (n^2+n-1))≈O(n^2) O(T)=O(21(n2+n1))O(n2)

状态最好情况最坏情况平均时间复杂度
时间复杂度   O ( n 2 ) \ O(n^2)  O(n2)   O ( n 2 ) \ O(n^2)  O(n2)   O ( n 2 ) \ O(n^2)  O(n2)

4.空间复杂度

选择排序是原地排序的一种排序,所以空间复杂度为O(1)

原地排序就是指在排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序。

5.稳定性

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。–百度百科

在上述的图表案例中,我已经指出,如果交换的条件: 只有 当前值<最小值 才交换
如下图所示,白色2 的位置和蓝色2的相对位置不会发生转变。
在这里插入图片描述

交换后的结果为:
在这里插入图片描述
因此,选择排序是一种稳定的排序。

6. 算法优化

优化的出发点:每次进行循环,都会选取最小值放在队头。也许可以左右开弓,每次选择最大最小两个值,分别放在队头和队尾。

6.1 图解算法

假设a=[1,3,2,9,7,2],下面对a进行升序排列。
初始化
指定最初的最大最小值分别为数组的开头和结尾。
在这里插入图片描述
遍历下标 从 0 到 5 分别找到最大最小值 如下:
在这里插入图片描述下面将最小值放在数组开始, 也就是交换最小值和0号位置元素。

在这里插入图片描述下面将最大值放在数组结尾, 也就是交换最大值和5号位置元素。
在这里插入图片描述你可能会发现,这个结果和预期的不太一样。
由于经过上一步,最小值已经被替换了,这时候在进行最大值交换,就会导致错位。
所以,如果遇到最大值所在位置,是最小值准备放的位置时,要将最小值的下标赋值给max_index 。
在这里插入图片描述下面将最大值放在数组结尾, 也就是交换最大值和5号位置元素。
在这里插入图片描述
初始化,max和min 开始下一次循环

在这里插入图片描述这样就完成了排序。主要是注意上述的错位问题

6.2 代码实现

def selecesort2(nums):
	for x in range(len(nums)//2):#从两端开始遍历,所以只需要中间长度就可以了
		min_,min_index=nums[x],x#标记最小值初始位置
		max_,max_index=nums[len(nums)-x-1],len(nums)-x-1#标记最大值开始位置
		for j in range(x,len(nums)-x):#查找最大最小值的范围 除两端已经排好的元素的中间部分
			if nums[j]<min_:#记录最小值
				min_,min_index=nums[j],j
			if nums[j]>max_:#记录最大值
				max_,max_index=nums[j],j

		if min_index != x:#进行最小值的交换
			nums[x],nums[min_index]=nums[min_index],nums[x]
		
		#这是一种特殊的情况,如果你记录的最大值的位置是 最小值的初始位置,但是经过上一步,最小值已经被替换了
		#这时候在进行最大值交换,就会导致错位。我在上面图示中已经写明。
		if  max_index==x:
			max_index=min_index

		if max_index != len(nums)-x-1 :#进行最大值交换
			nums[len(nums)-x-1],nums[max_index]=nums[max_index],nums[len(nums)-x-1]
	print(nums)


6.3 优化总结

只能说换了一种方式,优化后代码的执行速度并未有提高,甚至有时有所下降。

#经过测试
原代码执行100000次 用时0.142593s
优化后执行100000次 用时0.135219s
[Finished in 0.3s]

到此结束 打完收工。
如果有更好的方案,欢迎留言讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值