选择排序是一种最简单但也最没用(时间复杂度高且不稳定)的排序算法,无论什么数据进去都是 O(n²) 的时间复杂度。所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。也有优化的空间。
算法描述
- 在一个长度为 N 的无序数组中,第一次遍历 n-1 个数找到最小的和第一个数交换。
- 第二次从下一个数开始遍历 n-2 个数,找到最小的数和第二个数交换。
- 重复以上操作直到第 n-1 次遍历最小的数和第 n-1 个数交换,排序完成。
代码实现
static void SelectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minPos = i;
for (int j = i + 1; j < arr.length; j++) {
minPos = arr[j] < arr[minPos] ? j : minPos;
}
swap(arr, minPos, i);
}
print(arr);
}
static void swap(int[] arr, int i, int j){
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
static void print(int[] arr){
for(int i = 0; i < arr.length; i++){
System.out.print(" " + arr[i]);
}
System.out.println();
}
注意点
- 外循环的 i 之所以要小于 arr.length - 1 ,是因为内循环是从 i + 1 开始的,防止越界。不减一也不会报错,但最好有。
- 每次找到比 minPos 位置上的数还小的数时,记录他的角标,最后直接交换两个角标下的值,而不是每次找到就交换一次。
优化思路
最简单的选择排序是每次循环找到最小值与窗口最前面的值做交换。我们可以在循环的时候同时找到最大值,与窗口最后面的值做交换,这样就会节约一半时间。
优化代码实现
static void SelectSort2(int[] arr) {
for (int i = 0; i < arr.length - i; i++) {
intminPos = i;
int maxPos = arr.length - i - 1;
for (int j = i; j < arr.length - i; j++) {
minPos = arr[j] < arr[minPos] ? j : minPos;
maxPos = arr[j] > arr[maxPos] ? j : maxPos;
}
if (maxPos == i && minPos != arr.length - i - 1 ){
swap(arr, maxPos, arr.length - i - 1);
swap(arr, minPos, i);
} else if (minPos == arr.length - i - 1 && maxPos == i) {
swap(arr, minPos, i);
} else{
swap(arr, minPos, i);
swap(arr, maxPos, arr.length - i - 1);
}
}
print(arr);
}
注意点
- 每次都是同时找到最大值和最小值并交换。所以每次循环选择的窗口,前后的边界都需向中间收一格。
for (int i = 0; i < arr.length - i; i++)
- 可能出现最大值在左边界上的情况,所以每次循环须从左边界开始。
for (int j = i; j < arr.length - i; j++)
- 可能出现最大值在左边界,最小值在右边界的情况,此时只需一次交换。以及分别出现最小值在右边界和最大值在左边界的两种情况,需要注意交换顺序。
if (maxPos == i && minPos != arr.length - i - 1 ){
swap(arr, maxPos, arr.length - i - 1);
swap(arr, minPos, i);
} else if (minPos == arr.length - i - 1 && maxPos == i) {
swap(arr, minPos, i);
} else{
swap(arr, minPos, i);
swap(arr, maxPos, arr.length - i - 1);
}