选择排序
选择排序,一般我们指的是简单选择排序,你可以叫直接选择排序,他不是像冒泡排序一样相邻交换元素,而是通过选择一轮元素中最小的元素,进行交换,虽然交换次数少,但是效率和冒泡排序一样糟糕!
选择排序属于选择类排序算法。
算法介绍
🌰:5 9 1 6 8 14 6 49 25 4 6 3
注:
- 橙色表示已完成排序的元素
- 红色表示标记当前(最小/需要交换)元素
- 绿色表示扫描元素
这个排序过程很好理解:
- 第一轮迭代,从第一个数开始,从左边向右边进行扫描,找到最小的数,与第一个数进行交换
- 第二轮迭代,从第二个数开始,从左边到右边进行扫描,找到最小的数,与第二个数进行交换
- N 轮迭代:…
最后是可以得到结果:1 3 4 5 6 6 6 8 9 14 25 49。
上述的行为叫做简单选择排序。
选择排序是一个不稳定的排序算法,比如数组:[5 6 5 1],第一轮迭代时最小的数是 1,那么与第一个元素 5 交换位置,这样数字 1 就和数字 5 交换了位置,导致两个相同的数字 5 排序后位置变了。
时间复杂度
他每一轮的比较次数和冒泡排序是一样的,不同的是,他的每轮迭代交换次数减少到了 1 次。
总体的比较次数:1+2+3+4+...+N-1=(N^2 - N)/2,这是平方级别的时间复杂度,我们记为 O(n^2)。
空间复杂度
只用到了一个变量,所以是常量:O(1)
算法实现
func SelectSort(list []int) {
//第一层循环遍历每一轮需要交换的值
for i:=0;i<len(list);i++{
//记录最小元素的索引位置
minIndex :=i
//第二层循环扫描,获取最小的元素
for j:=i+1;j<len(list);j++{
//记录最小元素的索引
if list[j]<list[minIndex ]{
minIndex =j
}
}
//交换
list[i],list[minIndex ]=list[minIndex ],list[i]
}
}
其实整体上这个算法比较好懂的一个。
算法改进
func SelectSortPro(list []int) {
n:=len(list)
//第一层遍历 n/2 次数
for i:=0;i<n/2;i++{
//每一轮最小值的索引
minIndex:=i
//每一轮最大值索引
maxIndex:=i
//找到这一轮的最大值与最小值
for j:=i+1;j<n-i;j++{
if list[j]>list[maxIndex]{
maxIndex=j
}
if list[j]<list[minIndex]{
minIndex=j
}
}
//为什么不能直接允许下面的代码
//list[i],list[minIndex]=list[minIndex],list[i]
//list[n-i-1],list[maxIndex]=list[maxIndex],list[n-i-1]
/*
因为存在最大值在开头,如果你先最小值与开头值进行交换,就会把最大值换一个位置,导致最后无法交换正确
最小值一样,所以这里是有一定交换顺序的。
当最大值在开头处,应该先换最大值。
当最小值在结尾处,应该先换最小值。
当最小值在结尾,最大值在开头,两次交换会导致交换无效
其余情况顺序其实就无所谓了。
*/
if maxIndex==i&&minIndex!=n-i-1{
list[n-i-1],list[maxIndex]=list[maxIndex],list[n-i-1]
list[i],list[minIndex]=list[minIndex],list[i]
}else if maxIndex==i&&minIndex==n-i-1{
list[maxIndex],list[minIndex]=list[minIndex],list[maxIndex]
}else {
list[i],list[minIndex]=list[minIndex],list[i]
list[n-i-1],list[maxIndex]=list[maxIndex],list[n-i-1]
}
}
}
选择算法还是比较慢的,好理解,但是不建议选择使用。