一、选择排序的原理(默认排序为从小到大):
原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕。第i趟简单选择排序是指通过n-i次关键字的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录进行交换。先临时记录其位置,只有在一趟循环完毕以后确定了最小的数据,才会发生交换。
例:对 9 1 5 3 7 进行选择排序:
第一步:找到1最小,1和9交换位置:1 9 5 3 7
第二步:因为1是有序的,所以看后四个数字9 5 3 7,在这之中3是最小的,故3和9交换位置变为1 3 5 9 7
第三步:1 3有序,所以只看 5 9 7…
以此类推直到排序完成。
代码:
public static void _choiceSort(Integer[] a) {
if (a == null || a.length <= 0) {
return;
}
for (int i = 0; i < a.length; i++) {
int min = i;
//将当前下标定义为最小值下标
for (int j = i + 1; j < a.length; j++) {
if (a[min] > a[j]) {
//如果有小于当前最小值的关键字
min = j;
//将此关键字的下标赋值给min
}
}
if (i != min) {
//若min不等于i,说明找到最小值,交换
int tmp = a[min];
a[min] = a[i];
a[i] = tmp;
}
}
选择排序的时间复杂度是O(n2),空间复杂度是O(1),是一种不稳定的排序算法。
二、选择排序的优化:
之前我们的选择排序是每次选择一个最小的换到第一个位置,那么我们知道一列数字是有两端的,我们把将小的数字放在一端的话那么排好序后另一端所存放的数字自然应该是大的。
那么我们不如在排序的时候就有意去将最小的数字与第一个数字交换,将最大的数字与最后一个数字进行交换,这样就可以把选择排序的算法的效率提高到原来的两倍。
代码:
for(int i=0; i<=array.length/2; i++){
int minIndex = i; //保存待排序列最小值索引
int maxIndex = i; //保存待排序列最大值索引
//因为每次移动两个元素,所以排序的趟数是长度除以2
for(int j=i+1; j<=array.length-1-i; j++){
if(array[maxIndex] < array[j]){
maxIndex = j;
continue;
}
if(array[minIndex] > array[j]){
minIndex = j;
}
}//分别对数列中最大和最小进行一个定位,以便进行数值交换
int tmp = array[i];
array[i] = array[minIndex];
array[minIndex] = tmp;//最小值放到i位置
if(maxIndex == i){
maxIndex = minIndex;
}//应对特殊情况
tmp = array[array.length-1-i];
array[array.length-1-i] = array[maxIndex];
array[maxIndex] = tmp;//最大值放到array.length-1-i位置
}
下面讨论一下特殊情况:
在讨论特殊情况之前,先将优化后的选择排序的特点说明一下:
优化之后的选择排序实际上可以看做是一个希望双线同时执行的算法,但是我们实际写出来的程序并不是双线同时执行的的,所以实际上是一个伪双线执行的算法,是一个先后有序的算法。而这种先后有序会导致一些特殊情况,下面我会用图简单说明一下:
上边我们看到的是一种理想的情况,当先执行的黑色与后执行的蓝色在执行的路径上没有交集的时候,是不会出现任何特殊情况的。但是当出现交集的时候就会导致一些特殊的情况
交集1
该种交集在一个有序的数列中是不可能存在的!因为选择排序算法是先进行定位在进行数据的交换的,而一个有序的数列是不可能出现最大和最小都是同一个数的情况。
交集2
此种交集会导致一种特殊情况的出现:因为代表小端交换的黑色路线是先执行的,代表大端交换的蓝色路线是后执行的,这样就会导致在最开始执行的交换过程中把后边交换所需要的被定位到的最大值的数字进行了改变,这就是我们的伪双线运行带来的一种特殊情况,即先执行动作影响到后续执行动作。
根据该种特殊规律的总结,我们可以发现,如果最大值(后续路线交换数字)定位在了小端数字(先行交换路线)要进入的位置,也就是maxIndex == i这种情况时会出现。所以在代码中增加了将maxIndex修改会起始状态的情况
所以会导致先被交换到小端的最小数值会在后续的交换过程中交换到后方,从而导致本来应该在小端的数字出现在了大端。
交集3
此种交集虽然和交集2类似,但是却并不会出现错误,因为先执行的路线并未对之前定位好的数字产生任何影响。
故不会造成错误。