选择排序专栏
选择排序是最基础也最容易理解的排序算法之一,它的核心思想简单直接,非常适合排序算法的入门学习。本文将从原理、实现、特性到优化,全面解析选择排序算法。
一、选择排序的核心思想
选择排序的工作原理就像它的名字一样 ——"选择":在未排序的元素中找到最小(或最大)的那个元素,然后将它与未排序部分的第一个元素交换位置。重复这个过程,直到所有元素都被排序。
形象地说,就好像你在整理一手扑克牌:每次从剩余的牌中挑出最小的一张,按顺序放在左手边,直到所有牌都被挑完。
排序过程分解
以数组 [5, 2, 4, 6, 1, 3]
为例,升序排序过程如下:
- 初始状态:
[5, 2, 4, 6, 1, 3]
(全部未排序) - 第 1 轮:找到最小值 1,与第一个元素 5 交换 →
[1, 2, 4, 6, 5, 3]
- 第 2 轮:从剩余元素中找到最小值 2,位置不变 →
[1, 2, 4, 6, 5, 3]
- 第 3 轮:找到最小值 3,与 4 交换 →
[1, 2, 3, 6, 5, 4]
- 第 4 轮:找到最小值 4,与 6 交换 →
[1, 2, 3, 4, 5, 6]
- 第 5 轮:剩余元素已有序,排序完成
二、Java 实现选择排序
下面是选择排序的 Java 实现代码,包含详细注释:
public class Selection {public static void main(String[] args) {Integer[] a = {1,9,6,7,66,11,326,98};sort(a);System.out.println(Arrays.toString(a));}public static void sort(Comparable[] a){//定义i为首个数据for (int i = 0; i < a.length-1; i++) {//定义最小值为minint min = i;//for (int j = i+1; j < a.length; j++) {if(greater(a[min],a[j])){min = j;}}//得到最小的放到最前面exchange(a,i,min);}}//比较v是否大于n;public static boolean greater(Comparable v,Comparable n){//调用comparable的方法//v大于n是返回 1,//v等于n时返回 0,//v小时n时返回 -1int i = v.compareTo(n);return i==1;}//交换方法public static void exchange(Comparable[] a,int i,int j){Comparable temp = a[i];a[i] = a[j];a[j] = temp;}}
代码解析
上面的实现包含三个主要部分:
sort 方法:核心排序逻辑
- 外层循环控制需要排序的元素范围
- 内层循环在未排序部分中寻找最小值
- 找到最小值后与未排序部分的第一个元素交换
main 方法:测试排序算法,包含测试用例
exchange方法:传入对象,实现位置的交换
greater方法:实现数据比较
三、选择排序的特性分析
选择排序的时间复杂度主要由比较操作和交换操作两部分构成,其中比较操作占主导地位。
时间复杂度分析
对于长度为 n
的数组,选择排序的执行过程如下:
- 第 1 轮:需要比较
n-1
次(在 n 个元素中找最小值) - 第 2 轮:需要比较
n-2
次(在 n-1 个元素中找最小值) - ...
- 第 n-1 轮:需要比较
1
次
总比较次数为:(n-1) + (n-2) + ... + 1 = n(n-1)/2
,这是一个关于 n
的二次函数。
时间复杂度情况
- 最好情况:O (n²) - 即使数组已经有序,仍需完整遍历寻找最小值
- 最坏情况:O (n²) - 数组完全逆序时
- 平均情况:O(n²)
选择排序的时间复杂度不受输入数据的有序程度影响,这是它与冒泡排序、插入排序的重要区别。
因为不管你有没有序,我都要比较。
空间复杂度分析
选择排序的空间特性非常优秀:
- 它只需要几个额外的变量(如用于记录最小值索引的
minIndex
和交换用的临时变量temp
) - 排序过程在原数组上进行,不需要额外的数组或数据结构
因此,选择排序的空间复杂度为 O (1),属于原地排序(In-place Sorting) 算法。
稳定性
选择排序是不稳定的排序算法。
例如,对于数组 [2, 2, 1]
,排序后第一个 2 会被交换到最后,导致两个 2 的相对顺序改变。
交换次数
选择排序的交换次数很少,最多为 n-1 次(每轮最多交换一次),这是它相比冒泡排序的一个优势。
四、选择排序的优化思路
基础的选择排序每次只寻找一个最小值,我们可以优化为双向选择排序,同时寻找最小值和最大值:
双向选择排序的Java实现
public class Selection {public static void main(String[] args) {Integer[] a = {1,9,6,7,66,11,326,98};sort(a);System.out.println(Arrays.toString(a));}public static void sort(Comparable[] a){int left = 0;int right = a.length - 1;while (left < right) {int minIndex = left;int maxIndex = right;// 在未排序部分中同时找出最小值和最大值的索引for (int i = left; i <= right; i++) {if (greater(a[minIndex], a[i])) {minIndex = i;}if (greater(a[i], a[maxIndex])) {maxIndex = i;}}// 处理最小值if (minIndex != left) {exchange(a, minIndex, left);// 如果最大值在 left 位置,交换后最大值位置变为 minIndexif (maxIndex == left) {maxIndex = minIndex;}}// 处理最大值if (maxIndex != right) {exchange(a, maxIndex, right);}// 缩小未排序部分的范围left++;right--;}}// 比较v是否大于n;public static boolean greater(Comparable v, Comparable n){// 调用comparable的方法// v大于n时返回 1,// v等于n时返回 0,// v小于n时返回 -1int i = v.compareTo(n);return i > 0;}// 交换方法public static void exchange(Comparable[] a, int i, int j){Comparable temp = a[i];a[i] = a[j];a[j] = temp;}
}
双向选择排序的优势是将外层循环的次数减少了一半,虽然时间复杂度仍然是 O (n²),但实际运行效率有所提升。
五、选择排序的适用场景
选择排序虽然时间复杂度较高,但也有其适用场景:
- 数据量较小的情况:对于 n≤1000 的小规模数据,选择排序简单有效
- 内存空间有限的场景:原地排序特性使其适合内存受限环境
- 交换成本较高的场景:相比冒泡排序,选择排序的交换次数少得多
在实际开发中,Java 标准库中的排序算法(如 Arrays.sort ())在不同场景下会使用不同的排序策略,但对于理解排序算法的基本思想,选择排序是一个很好的起点。
总结
选择排序是一种简单直观的排序算法,其核心思想是 "选择最小(大)元素,放到正确位置"。虽然时间复杂度为 O (n²),不适合大规模数据排序,但它实现简单、空间复杂度低、交换次数少的特点,使其在特定场景下仍有应用价值。