选择排序
步骤
- 属于选择排序
- 两两比较大小, 找出极值放在固定的位置(某一端)
- 结果分为升序和降序
- 降序
- n个数,从左到右索引0到n-1, 两两一次比较,记录最大值索引,此轮比较结束后将最大值和索引为0的数交换, 如果最大数是索引0,则不交换
- 第二轮从索引1开始,同理,每次左边都会固定下一个数
- 升序
- 和降序相反
简介
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置(升序或者降序),然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换,所以它的时间复杂度是n(n-1),不包括这个函数的低阶项和首项系数后用大O表示法为:O(n^2)。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种
选择排序实现
-
简单选择排序
nums = [2,3,4,5,6,7,8] length = len(nums) count_iter = 0 count_swap = 0 for i in range(length): maxindex = i for j in range(i + 1, length): count_iter += 1 if nums[j] > nums[maxindex]: maxindex = j if i != maxindex: nums[i], nums[maxindex] = nums[maxindex], nums[i] count_swap += 1 print(nums, count_iter, count_swap)
-
二元选择排序
-
同时固定最大值和最小值
-
优点:减少迭代元素的次数
nums = [2,3,4,5,5,6,1,2,3,6,7,8] length = len(nums) count_iter = 0 count_swap = 0 for i in range(length): maxindex = i minindex = -i - 1 for j in range(i + 1, length): count_iter += 1 if nums[j] > nums[maxindex]: maxindex = j if nums[-j - 1] < nums[minindex]: minindex = -j - 1 if maxindex != i: nums[i], nums[maxindex] = nums[maxindex], nums[i] count_swap += 1 if minindex != -i - 1: nums[-i -1], nums[minindex] = nums[minindex], nums[-i - 1] count_swap += 1 print(nums, count_iter, count_swap)
-
上面虽然已经完成了二元选择排序,但是结果却不是正确的,为什么呢?想一下:如果最小值所在的位置刚好是最大值要交换的位置时,如果直接交换最大值,那么最小值的索引就会变为原最大值的索引。针对这种情况在交换最大值时判断一下最大值要交换的位置是否等于最小值所在的位置,如果是,那么需要在最大值修改完毕后,将最小值的索引进行重置(重置为最大值所在的索引)
-
-
优化索引,将负索引修正为正索引
nums = [2,3,4,5,5,6,1,2,3,6,7,8] length = len(nums) count_iter = 0 count_swap = 0 for i in range(length // 2): #每次循环长度减2,通过运算即可发现规律 maxindex = i minindex = -i - 1 min_origin = length + minindex for j in range(i + 1, length - i):#每次左右都要少一个 count_iter += 1 if nums[j] > nums[maxindex]: maxindex = j if nums[-j - 1] < nums[minindex]: minindex = -j - 1 else: minindex = length + minindex #调整为正索引 if maxindex != i: nums[i], nums[maxindex] = nums[maxindex], nums[i] count_swap += 1 if i == minindex: #最小值被交换过,就需要更新索引 minindex = maxindex if minindex != min_origin: nums[min_origin], nums[minindex] = nums[minindex], nums[min_origin] count_swap += 1 print(nums, count_iter, count_swap)
-
如果一轮比较后,极大值和极小值的值相等,说明元素全部相等
nums = [2,3,4,5,5,6,1,2,3,6,7,8] length = len(nums) count_iter = 0 count_swap = 0 for i in range(length // 2): maxindex = i minindex = -i - 1 min_origin = length + minindex for j in range(i + 1, length - i):#每次左右都要少一个 count_iter += 1 if nums[j] > nums[maxindex]: maxindex = j if nums[-j - 1] < nums[minindex]: minindex = -j - 1 if nums[maxindex] == nums[minindex]:break #元素都相同 minindex = length + minindex #调整为正索引 if maxindex != i: nums[i], nums[maxindex] = nums[maxindex], nums[i] count_swap += 1 if i == minindex: #最小值被交换过,就需要更新索引 minindex = maxindex if minindex != min_origin: nums[min_origin], nums[minindex] = nums[minindex], nums[min_origin] count_swap += 1 print(nums, count_iter, count_swap)
-
减少最大或者最小值的无用交换
nums = [1,1,1,1,1,2] #nums = [2,1,1,1,1,1,1] 同理,增加最大值的判断 length = len(nums) count_iter = 0 count_swap = 0 for i in range(length // 2): maxindex = i minindex = -i - 1 min_origin = length + minindex for j in range(i + 1, length - i):#每次左右都要少一个 count_iter += 1 if nums[j] > nums[maxindex]: maxindex = j if nums[-j - 1] < nums[minindex]: minindex = -j - 1 if nums[maxindex] == nums[minindex]:break #元素都相同 minindex = length + minindex #调整为正索引 if maxindex != i: nums[i], nums[maxindex] = nums[maxindex], nums[i] count_swap += 1 if i == minindex: #最小值被交换过,就需要更新索引 minindex = maxindex if minindex != min_origin and nums[min_origin] != nums[minindex]: nums[min_origin], nums[minindex] = nums[minindex], nums[min_origin] count_swap += 1 print(nums, count_iter, count_swap)
总结
- 简单选择排序需要一轮轮比较,在每一轮比较中发现极值
- 无法知道当前轮是否已经达到排序的要求,但可以知道极值是否在目标索引位置上
- 遍历次数1,…,n-1之和n(n-1)/2
- 时间复杂度O(n**2)
- 减少了交换次数,提高了效率,性能略好于冒泡法