选择排序是继冒泡排序后另一个经典的入门算法,其“打擂台”式的思想非常直观。
🆚 选择排序 vs. 冒泡排序
为了建立联系和对比,我们先看看它和冒泡排序有何不同。
| 特性 | 选择排序 (Selection Sort) | 冒泡排序 (Bubble Sort) |
|---|---|---|
| 核心思想 | 选择:每轮从未排序部分选出最小(或最大)元素,直接放到正确位置。 | 交换:通过相邻元素两两比较和交换,使最大(或最小)元素像气泡一样“冒”到顶端。 |
| 比较次数 | 固定为 O(n²),与数据初始状态无关。 | 平均和最坏情况下为 O(n²),但在最好情况(已有序)下可优化至 O(n)。 |
| 交换次数 | 最多 n-1 次,交换次数少。 | 平均和最坏情况下为 O(n²),交换操作更频繁。 |
| 稳定性 | 不稳定排序。相等元素在选择过程中的相对位置可能改变 。 | 稳定排序。只有相邻元素逆序时才交换,相等元素不会交换。 |
| 性能倾向 | 由于交换次数少,在写操作开销大(如写入Flash存储器)时更有优势。 | 在数据基本有序时,经过优化的冒泡排序效率较高。 |
简单来说,选择排序的聪明之处在于它“谋定而后动”——先找到最小元素,再一次性交换到位。
💻 选择排序的Java实现
这里是选择排序的标准Java实现,代码逻辑非常清晰。
import java.util.Arrays;
public class SelectionSort {
public static void selectionSort(int[] 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] < arr[minIndex]) {
minIndex = j;
}
}
// 一轮比较完成后,将找到的真正最小值与当前轮次的第一个元素交换
if (minIndex != i) {
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
// 此时,arr[i] 及之前的元素已处于其最终排序位置
}
}
public static void main(String[] args) {
int[] arrayToSort = {64, 25, 12, 22, 11};
System.out.println("排序前: " + Arrays.toString(arrayToSort));
selectionSort(arrayToSort);
System.out.println("排序后: " + Arrays.toString(arrayToSort));
}
}
🔍 算法步骤拆解
让我们用例子 [64, 25, 12, 22, 11]来一步步拆解选择排序的过程 :
-
第1轮 (i=0):
- 未排序部分:
[64, 25, 12, 22, 11] - 找到最小值
11(索引4),与第一个元素64交换。 - 结果:
[11, 25, 12, 22, 64]// 最小值11就位
- 未排序部分:
-
第2轮 (i=1):
- 未排序部分:
[25, 12, 22, 64]//11已是已排序部分 - 找到最小值
12(索引2),与25交换。 - 结果:
[11, 12, 25, 22, 64]// 次小值12就位
- 未排序部分:
-
第3轮 (i=2):
- 未排序部分:
[25, 22, 64] - 找到最小值
22(索引3),与25交换。 - 结果:
[11, 12, 22, 25, 64]
- 未排序部分:
-
第4轮 (i=3):
- 未排序部分:
[25, 64] - 最小值就是
25,无需交换。 - 结果:
[11, 12, 22, 25, 64]// 排序完成
- 未排序部分:
⚙️ 算法特性总结
理解这些特性有助于你在不同场景下选择合适的算法。
-
时间复杂度:O(n²)。无论数据初始是否有序,它都需要进行 n(n-1)/2 次比较 。
-
空间复杂度:O(1)。是原地排序,只需要常数级别的额外空间用于临时变量 。
-
稳定性:不稳定。这是选择排序的一个关键特点。例如序列
[5, 8, 5, 2],第一轮会选择2与第一个5交换,导致两个5的相对顺序改变 。 -
优缺点:
- 优点:思想简单,易于理解和实现;交换次数少,在数据量小或写操作成本高时有优势 。
- 缺点:时间复杂度高,不适合大规模数据排序;不稳定。
💎 如何选择
- 当处理小规模数据,或者你对代码的简洁性和可读性要求更高时,选择排序是个不错的候选。
- 当需要稳定的排序或数据量较大时,应考虑插入排序、归并排序等更高效的算法。