什么是选择排序?
比如对于一个无序的数组,想让它从小到大排列,则使用选择排序规则如下:
1.首先遍历整个数组,找到最小的元素,将它与数组第一个元素交换位置,完成第一轮交换
2.经过第1步,最小的元素已经放在了第一位,接下来可以固定刚才选出来的第一个元素,而从第2个元素开始,同样遍历后面的元素,找到其中最小的元素,将其与数组第2个元素交换,即此时已经将倒数第二小的元素放在了数组第2位,完成第二轮交换
3.重复前面的步骤,即固定前两个元素,后面的元素则同样遍历,取出其中最小的元素放在数组第3位。
4.以此类推,直到数组最高位,最终得到一个从小到大的数组。
比如对于一个无序的数组,想让它从小到大排列,则使用选择排序规则如下:
1.首先遍历整个数组,找到最小的元素,将它与数组第一个元素交换位置,完成第一轮交换
2.经过第1步,最小的元素已经放在了第一位,接下来可以固定刚才选出来的第一个元素,而从第2个元素开始,同样遍历后面的元素,找到其中最小的元素,将其与数组第2个元素交换,即此时已经将倒数第二小的元素放在了数组第2位,完成第二轮交换
3.重复前面的步骤,即固定前两个元素,后面的元素则同样遍历,取出其中最小的元素放在数组第3位。
4.以此类推,直到数组最高位,最终得到一个从小到大的数组。
以下是选择排序代码:
public void selectsort(int[] a){
int finishKey; //已经完成交换的下标
int selectKey; //充当遍历途中的当前元素的下标
int min; //存储当前最小位
//这一层循环表示从已经完成的位数开始每一轮的遍历,即每一轮遍历的下限
for(finishKey=0;finishKey<a.length-1;finishKey++){
min = finishKey; //一开始默认finishKey为最小值的下标
//从已经完成的位数之后开始遍历
for(selectKey=finishKey+1;selectKey<a.length;selectKey++){
if(a[selectKey]<a[min]){
//如果遍历途中有比a[min]还小的值,则将其下标赋给min
min = selectKey;
}
}
//每一轮遍历结束时都要将min与finishKey交换
int temp = a[finishKey];
a[finishKey] = a[min];
a[min] = temp;
}
}
测试代码:
测试结果:
注:按照选择排序的规则,需要有3个标志位:
1.已经完成排序的下标,在代码中即表现为:finishKey
2.遍历途中的游标,在代码中即表现为:selectKey
3.遍历一轮下来的最小位,在代码中即表现为:min
1.已经完成排序的下标,在代码中即表现为:finishKey
2.遍历途中的游标,在代码中即表现为:selectKey
3.遍历一轮下来的最小位,在代码中即表现为:min
其中,finishKey初始化为0,因为第一轮排序要从下标为0的元素开始,遍历前,默认finishKey为当前最小值
在内层遍历中,从已经完成排序的位数后开始遍历,即selectKey初始化为finishKey+1,如果途中发现有比a[min]更小的值,则将当前元素的下标即selectKey赋给min,保证min存储的是这一轮遍历中最小值的下标
完成一轮交换后,需要将这一轮遍历所得到的a[min]与a[finishKey],即将其放在这一轮遍历的头部
依此类推,完成每一轮的交换
在内层遍历中,从已经完成排序的位数后开始遍历,即selectKey初始化为finishKey+1,如果途中发现有比a[min]更小的值,则将当前元素的下标即selectKey赋给min,保证min存储的是这一轮遍历中最小值的下标
完成一轮交换后,需要将这一轮遍历所得到的a[min]与a[finishKey],即将其放在这一轮遍历的头部
依此类推,完成每一轮的交换
选择排序的时间复杂度:
可以看到,选择排序同样有两层循环,它的时间复杂度与冒泡排序的时间复杂度基本一致,都为O(N^2),但是选择排序的交换次数是比较少的,特别是在数据量很大的时候,例如对于100个数据项,冒泡排序平均用了(N^2)/4次,即2500次,而选择排序则只需要少于100次的交换,所以选择排序无疑更快
可以看到,选择排序同样有两层循环,它的时间复杂度与冒泡排序的时间复杂度基本一致,都为O(N^2),但是选择排序的交换次数是比较少的,特别是在数据量很大的时候,例如对于100个数据项,冒泡排序平均用了(N^2)/4次,即2500次,而选择排序则只需要少于100次的交换,所以选择排序无疑更快