文章目录
1 选择排序介绍
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置(升序或者降序),然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n
个元素的表进行排序总共进行至多n-1
次交换,所以它的时间复杂度是n(n-1)
,不包括这个函数的低阶项和首项系数后用大O表示法为:O(n^2)
。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
2 基本选择排序
现有序列[1, 9, 8, 5, 6, 7, 4, 3, 2],请使用选择排序对该列表进行排序。
nums = [1, 9, 8, 5, 6, 7, 4, 3, 2]
length = len(nums)
for i in range(length-1):
max_index=i
for j in range(i+1,length):
if nums[max_index] < nums[j]:
max_index = j
if i != j:
nums[max_index], nums[i] = nums[i], nums[max_index]
print(nums)
3 选择排序的优化
上面的代码,仅仅是针对题中的列表进行的定制化
选择排序,真正的要适用各种类型的数字序列,还需要进行进一步优化。
3.1 二元选择排序
每一次循环找到一个最大值或最小值,存放在排序序列的起始位中去,我们知道,每次在未排序区查找时,都会进行遍历,那么必然会发现最大值,同时也会发现最小值,那么我们是否可以同时对最大值和最小值进行排序呢?
3.2 同时排序最大和最小值
由于一次循环要同时判断最大值和最小值,由于循环是从左边开始的,所以最大值的初始位置和i的取值是相同的,但最小值的初始值是从末尾开始,逐渐向右的,所以这里启用一个变量记录最小值的位置便于交换。
nums = [1, 9, 8, 5, 6, 7, 4, 3, 2]
#正索引 0 1 2 3 4 5 6 7 8
#负索引-9 -8 -7 -6 -5 -4 -3 -2 -1
length = len(nums)
for i in range(length - 1):
max_index = i
min_index = -i - 1
source_min = min_index # 1
for j in range(i+1, length):
if nums[max_index] < nums[j]:
max_index = j
if nums[min_index] > nums[-j - 1]:
min_index = -j - 1
if max_index != i:
nums[max_index], nums[i] = nums[i], nums[max_index]
if min_index != source_min:
nums[min_index], nums[source_min] = nums[source_min], nums[min_index]
print(nums)
3.3 索引混乱
上面虽然已经完成了二元选择排序,但是结果却不是正确的,为什么呢?想一下:如果最小值所在的位置刚好是最大值要交换的位置时,如果直接交换最大值,那么最小值的索引就会变为原最大值的索引。针对这种情况在交换最大值时判断一下最大值要交换的位置是否等于最小值所在的位置,如果是,那么需要在最大值修改完毕后,将最小值的索引进行重置(重置为最大值所在的索引)
nums = [1, 9, 8, 5, 6, 7, 4, 3, 2]
#正索引 0 1 2 3 4 5 6 7 8
#负索引-9 -8 -7 -6 -5 -4 -3 -2 -1
length = len(nums)
for i in range(length - 1):
max_index = i
min_index = -i - 1
source_min = min_index
for j in range(i+1, length):
if nums[max_index] < nums[j]:
max_index = j
if nums[min_index] > nums[-j - 1]:
min_index = -j - 1
if max_index != i:
nums[max_index], nums[i] = nums[i], nums[max_index]
if (length + min_index) == i:
min_index = max_index
if min_index != source_min:
nums[min_index], nums[source_min] = nums[source_min], nums[min_index]
print(nums)
3.4 代码优化
- 思考点1:既然我们一趟可以确定两个元素(1个最大值,1个最小值),那我们还需要length-1次循环吗? 如果元素为7个,只需要判断3次即可,如果元素是8个,只需要判断4次即可,根据以上条件得知,只需要循环length//2次就可以完成排序了。
- 思考点2:在一次循环的结果中最大值或最小值和要排的位置的值是相同时,就不需要进行修改了。
- 思考点3:如果在一次循环中最大值索引和最小值索引对应的元素相同,比如[4,2,1,1,1,1,3] 这种情况下,[1,1,1,1] 就不需要排序了。
nums = [1, 9, 8, 5, 6, 7, 4, 3, 2]
# nums = [4, 3, 1, 1, 2, 1, 1, 5, 6, 2]
# 正索引 0 1 2 3 4 5 6 7 8
# 负索引-9 -8 -7 -6 -5 -4 -3 -2 -1
count = 0
swap = 0
length = len(nums)
for i in range(length - 1): # 优化循环次数
max_index = i
min_index = -i - 1
source_min = min_index # 1
for j in range(i + 1, length):
count += 1
if nums[max_index] < nums[j]:
max_index = j
if nums[min_index] > nums[-j - 1]:
min_index = -j - 1
if nums[max_index] == nums[min_index]: # 最大最小值相同,则排序完毕
break
if max_index != i:
nums[max_index], nums[i] = nums[i], nums[max_index]
swap += 1
if (length + min_index) == i:
min_index = max_index
if min_index != source_min and nums[min_index] != nums[source_min]: # 如果值相同,就不需要交换了
swap += 1
nums[min_index], nums[source_min] = nums[source_min], nums[min_index]
print(count,swap)
print(nums)