选择排序是入门级排序算法中极具代表性的一种,它思路简单直白,核心是 “选择”—— 每一轮从待排序区间选出最值元素,放到指定位置。相较于冒泡排序,选择排序交换次数更少,是理解 “选择 - 交换” 排序逻辑的核心范例,也是面试中常考的基础考点。这篇文章带你清晰掌握选择排序的原理、核心实现,以及和冒泡排序的关键区别。
一、选择排序是什么?
选择排序,顾名思义,核心是 “选择”:将数组分为已排序区间和未排序区间,每一轮从未排序区间中找到最小(或最大)元素,放到已排序区间的末尾,直到整个数组有序。
核心思想:
分区间处理,每轮只做 1 次交换(找到最值后交换),而非像冒泡那样频繁交换相邻元素,以减少交换次数提升效率。
效率与特性:
- 时间复杂度:O(n²)(最坏 / 平均 / 最优,无论数组是否有序,都需遍历找最值)
- 空间复杂度:O(1)(原地排序,无额外空间消耗)
- 排序特性:不稳定排序(相等元素相对位置可能改变)、原地排序
适用场景:
适合小数据量排序,或对交换次数敏感、对稳定性无要求的场景;常作为入门案例,对比理解 “选择” 与 “冒泡” 的核心差异。
二、核心实现方式
选择排序无复杂变体,核心实现聚焦 “分区间找最值 + 交换”,以下是标准且易理解的实现版本:
import java.util.Arrays;
/**
* 选择排序标准实现(升序)
* @param arr 待排序数组
*/
public static void selectionSort(int[] arr) {
// 数组为空或长度为1,无需排序
if (arr == null || arr.length <= 1) {
return;
}
int n = arr.length;
// 外层循环:划分已排序区间,i为已排序区间的末尾下标
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;
}
}
}
// 调用示例
public static void main(String[] args) {
int[] arr = {5, 2, 9, 3, 7, 6, 1};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // 输出[1, 2, 3, 5, 6, 7, 9]
}
三、必避的三个核心坑
选择排序代码虽短,但新手易踩这些细节坑,记住这 3 点:
1、最值下标初始化: 必须初始化为 i(已排序区间末尾),而非 0,否则会重复处理已排序元素;
2、交换时机:需先遍历完未排序区间找到最值下标,再做 1 次交换,而非边比较边交换(否则退化为类似冒泡的逻辑);
3、稳定性问题:选择排序是不稳定排序,例如数组 [2, 3, 2],第一轮选到最小值 2(下标 2),与下标 0 交换后,两个 2 的相对位置改变,需注意业务场景对稳定性的要求。
四、总结
1、选择排序核心逻辑是 “分区间找最值 + 单次交换”,相较于冒泡排序交换次数更少;
2、选择排序时间复杂度始终为 O (n²),是不稳定排序,工程中适合小数据量、对稳定性无要求的场景;
3、核心避坑点是最值下标初始化、交换时机,以及明确其 “不稳定” 的特性,避免误用在对元素相对位置有要求的场景。