基础算法介绍(三)简单选择排序

一 简单选择排序

简单选择排序是一种直观且易于理解的基于比较的排序算法。核心特性如下

1.1 特性总结

特性 说明
核心思想 通过重复查找未排序部分的最小(或最大)元素,将其与未排序部分的首元素交换,从而逐步构建有序序列
时间复杂度 最好、最坏、平均情况下均为 O(n²)。比较次数固定为 n(n-1)/2 次
空间复杂度 O(1),是原地排序算法,仅需常数级别的额外临时空间
稳定性 不稳定。因交换操作可能改变相等元素的原始相对顺序
主要优势 实现简单直观;交换次数少(最多 n-1 次);不占用额外内存
主要劣势 时间复杂度高,不适合大规模数据;无法利用输入数据的现有顺序进行优化

1.2 算法原理

简单选择排序的运作过程可以清晰地分为“选择”和“交换”两大阶段。下图以数组 [64, 25, 12, 22, 11]为例,展示了其排序过程:
图片

1.2.1 初始化​​

将整个序列视为未排序序列。

1.2.2 查找最小值​​

遍历当前未排序序列,找到其中最小元素的下标。

1.2.3 交换放置​​

将找到的最小元素与未排序序列的第一个元素进行交换。此时,该最小元素就成为了已排序序列的最后一个元素。
​​缩小范围并重复​​:将未排序序列的边界向后移动一位(已排序序列长度+1),重复步骤2和3,直到未排序序列只剩一个元素(此时整个序列已有序)

1.3 复杂度分析

1.3.1 时间复杂度 O(n²) 的由来​​

算法包含两层循环。外层循环执行 n-1 次。内层循环用于查找最小值,第一次需要比较 n-1 次,第二次 n-2 次,依此类推。总的比较次数为 (n-1) + (n-2) + ... + 1 = n(n-1)/2,属于平方级别。​​无论数据的初始状态是有序、逆序还是随机,比较次数都相同​​,这是其效率不高的主要原因。交换操作最多发生 n-1 次。
​​

1.3.2 空间复杂度 O(1) 的由来

​​排序过程仅在原始数组内通过交换元素完成,只需要固定数量的临时变量(如用于交换的 temp,记录最小值的 minIndex),不随待排序数据规模 n 的增大而增加额外空间。

1.4使用场景

尽管时间复杂度较高,简单选择排序在以下特定场景中仍有其价值:

  • ​​小规模数据排序​​:当待排序元素数量很少(例如 n < 50)时,其实现简单的优势凸显,性能开销也可接受。
  • 内存受限的环境​​:由于是原地排序,空间复杂度为 O(1),适用于嵌入式系统等内存紧张的场景。
  • 交换操作成本高的场景​​:如果数据元素是大型结构体,交换(写操作)的成本远高于比较(读操作)的成本,选择排序交换次数少的优势就体现出来。
  • 算法教学与入门​​:由于其逻辑非常清晰,是理解排序算法基本思想和“选择”策略的经典案例。

1.5 代码实现

1.5.1 c 语言实现

#include <stdio.h>

// 简单选择排序函数
void selectionSort(int arr[], int n) {
    int i, j, minIndex, temp;

    // 外层循环:控制已排序序列的末尾位置,从0到n-2(共n-1趟)
    for (i = 0; i < n - 1; i++) {
        minIndex = i; // 假设当前未排序部分的第一个元素是最小值

        // 内层循环:在未排序部分arr[i+1..n-1]中查找真正的最小值
        for (j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j; // 更新最小元素的下标
            }
        }

        // 如果找到的最小值不在当前位置i,则交换
        if (minIndex != i) {
            temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
        // 此时,arr[0]到arr[i]已经是有序的
    }
}

// 打印数组函数
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// 主函数测试
int main() {
    int arr[] = {64, 25, 12, 22, 11};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的数组: \n");
    printArray(arr, n);

    selectionSort(arr, n);

    printf("排序后的数组: \n");
    printArray(arr, n);
    return 0;
}

1.5.2 ​代码关键点解释​​:

  • minIndex:用于记录每一趟遍历中当前找到的最小元素的索引。
  • 外层循环 i:表示已排序序列的末尾,也从0开始,到 n-2 结束(因为最后一个元素无需再排序)。
  • 内层循环 j:从 i+1开始,遍历剩余的未排序部分,寻找比当前 arr[minIndex]更小的元素。
  • 交换操作:在确认本轮最小元素后,将其与位置 i的元素交换。交换前判断 minIndex != i是一个小优化,避免不必要的自我交换。

1.6 与常用算法比较

排序算法详细对比

算法 时间复杂度 空间复杂度 稳定性 核心优势 主要劣势 最佳适用场景
简单选择排序 平均: O(n²)
最坏: O(n²)
最好: O(n²)
O(1) ❌ 不稳定 - 交换次数最少
- 实现简单
- 内存占用极低
- 性能稳定较差
- 无法利用数据有序性
- 小规模数据
- 交换成本高的场景
- 教学演示
冒泡排序 平均: O(n²)
最坏: O(n²)
最好: O(n)
O(1) ✅ 稳定 - 实现极其简单
- 稳定排序
- 可优化提前结束
- 交换次数过多
- 实际效率低
- 教学入门
- 小规模数据
- 部分有序数据
插入排序 平均: O(n²)
最坏: O(n²)
最好: O(n)
O(1) ✅ 稳定 - 小数据效率高
- 自适应排序
- 稳定排序
- 大规模数据效率低
- 数据逆序时性能差
- 小规模数据
- 基本有序数据
- 作为高级算法的子过程
堆排序 平均: O(n log n)
最坏: O(n log n)
最好: O(n log n)
O(1) ❌ 不稳定 - 最坏情况有保证
- 原地排序
- 适合大数据
- 实现复杂
- 缓存不友好
- 常数因子大
- 大规模数据排序
- 内存受限环境
- 实时系统

1.7 总结

简单选择排序是一种概念简单、编码容易的排序算法,其​​原地排序​​(O(1)空间复杂度)和​​交换次数少​​的特性使其在特定场景(如小规模数据、交换成本高、内存受限)下仍有应用价值。然而,其O(n²)的时间复杂度决定了它​​不适用于处理大规模数据​​。它的核心价值在于​​教学演示​​和作为理解更复杂算法(如堆排序)的基础。

posted on 2025-10-30 15:09  weiwei2021  阅读(4)  评论(0)    收藏  举报