选择排序

138 阅读4分钟

选择排序是继冒泡排序后另一个经典的入门算法,其“打擂台”式的思想非常直观。

🆚 选择排序 vs. 冒泡排序

为了建立联系和对比,我们先看看它和冒泡排序有何不同。

特性选择排序 (Selection Sort)冒泡排序 (Bubble Sort)
核心思想选择​:每轮从未排序部分选出最小(或最大)元素,直接放到正确位置。交换​:通过相邻元素两两比较和交换,使最大(或最小)元素像气泡一样“冒”到顶端。
比较次数固定为 O(n²),与数据初始状态无关。平均和最坏情况下为 O(n²),但在最好情况(已有序)下可优化至 O(n)。
交换次数最多 n-1 次,交换次数少。平均和最坏情况下为 O(n²),交换操作更频繁。
稳定性不稳定排序。相等元素在选择过程中的相对位置可能改变 。稳定排序。只有相邻元素逆序时才交换,相等元素不会交换。
性能倾向由于交换次数少,在写操作开销大​(如写入Flash存储器)时更有优势。在数据基本有序时,经过优化的冒泡排序效率较高。

简单来说,选择排序的聪明之处在于它“谋定而后动”——先找到最小元素,再一次性交换到位。

76339a259488423b03370e381ca5edb0.gif

💻 选择排序的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的相对顺序改变 。

  • 优缺点​:

    • 优点​:思想简单,易于理解和实现;交换次数少,在数据量小或写操作成本高时有优势 。
    • 缺点​:时间复杂度高,不适合大规模数据排序;不稳定。

💎 如何选择

  • 当处理小规模数据,或者你对代码的简洁性和可读性要求更高时,选择排序是个不错的候选。
  • 当需要稳定的排序或数据量较大时,应考虑插入排序、归并排序等更高效的算法。