选择排序的历史同样悠久,其基本思想——"每次选择最小元素放到前面"——可以追溯到人类手动排序的原始方法。在计算机科学中,选择排序是最早被形式化描述的排序算法之一。它的优势在于交换次数最少,对于交换成本较高的场景(如大对象的排序)有一定价值。
选择排序的基本思想是:将数组分为已排序区间和未排序区间。每一轮从未排序区间中找到最小元素,将其与未排序区间的第一个元素交换位置。这样,已排序区间就扩展了一个元素。重复这个过程,直到所有元素都排序完毕。
public class SelectionSort {
/**
* 选择排序
* @param arr 待排序的整数数组
*/
public static void sort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
int minIndex = i;
// 在未排序区间 [i, n-1] 中查找最小元素
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
// 将最小元素交换到已排序区间的末尾
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
/**
* 选择排序(泛型版本)
*/
public static <T extends Comparable<T>> void sortGeneric(T[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < n; j++) {
if (arr[j].compareTo(arr[minIndex]) < 0) {
minIndex = j;
}
}
if (minIndex != i) {
T temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
/**
* 双向选择排序
* 每轮同时找出最小值和最大值,分别放到两端
*/
public static void sortBidirectional(int[] arr) {
int left = 0, right = arr.length - 1;
while (left < right) {
int minIndex = left, maxIndex = right;
for (int i = left; i <= right; i++) {
if (arr[i] < arr[minIndex]) minIndex = i;
if (arr[i] > arr[maxIndex]) maxIndex = i;
}
// 注意处理边界情况
int temp = arr[left];
arr[left] = arr[minIndex];
arr[minIndex] = temp;
if (maxIndex == left) {
maxIndex = minIndex;
}
temp = arr[right];
arr[right] = arr[maxIndex];
arr[maxIndex] = temp;
left++;
right--;
}
}
}
| 指标 | 最好情况 | 最坏情况 | 平均情况 |
|---|---|---|---|
| 时间复杂度 | O(n²) | O(n²) | O(n²) |
| 空间复杂度 | O(1) | O(1) | O(1) |
| 比较次数 | n(n-1)/2 | n(n-1)/2 | n(n-1)/2 |
| 交换次数 | 0 | n-1 | n-1 |
选择排序是不稳定的排序算法(例如 [5a, 5b, 3] 排序后 5a 和 5b 的相对顺序可能改变),但是原地排序。它的最大特点是交换次数固定为 n-1 次,是所有基于比较的排序算法中交换次数最少的。