选择排序:原理、实现与优化

选择排序(Selection Sort)是入门级的排序算法之一,它的核心思想简单易懂,实现成本低,非常适合编程新手理解排序的基本逻辑。本文将从原理、C 语言实现、性能分析到优化思路,全方位讲解选择排序。

一、选择排序的核心原理

选择排序的核心可以概括为 **"找最值,放位置"**,它将数组分为 "已排序区间" 和 "未排序区间":

  1. 初始时,已排序区间为空,未排序区间为整个数组;

  2. 遍历未排序区间,找到其中的最小值(或最大值);

  3. 将找到的最值与未排序区间的第一个元素交换位置,此时该元素归入已排序区间;

  4. 重复步骤 2-3,直到未排序区间为空

    直观示例

    以数组 [5, 2, 9, 1, 5, 6] 为例,选择排序的执行过程:

  5. 第 1 轮:未排序区间 [5,2,9,1,5,6],最小值是 1,与第一个元素 5 交换 → [1, 2, 9, 5, 5, 6]

  6. 第 2 轮:未排序区间 [2,9,5,5,6],最小值是 2,无需交换 → [1, 2, 9, 5, 5, 6]

  7. 第 3 轮:未排序区间 [9,5,5,6],最小值是 5,与 9 交换 → [1, 2, 5, 9, 5, 6]

  8. 第 4 轮:未排序区间 [9,5,6],最小值是 5,与 9 交换 → [1, 2, 5, 5, 9, 6]

  9. 第 5 轮:未排序区间 [9,6],最小值是 6,与 9 交换 → [1, 2, 5, 5, 6, 9]

  10. 第 6 轮:未排序区间仅剩一个元素,排序完成。二、C 语言实现选择排序(升序)

二、C 语言实现选择排序(升序)

cs 复制代码
#include <stdio.h>

// 交换两个整数的值
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 选择排序(升序)
void selectionSort(int arr[], int n) {
    // 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) { // 优化:避免自身交换
            swap(&arr[i], &arr[minIndex]);
        }
    }
}

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

// 主函数测试
int main() {
    int arr[] = {5, 2, 9, 1, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    printf("排序前的数组:");
    printArray(arr, n);
    
    selectionSort(arr, n);
    
    printf("排序后的数组:");
    printArray(arr, n);
    
    return 0;
}

三、选择排序的性能分析

1. 时间复杂度

  • 最好情况:数组已有序,时间复杂度仍为 O(n2)(必须遍历找最值,无法提前终止);
  • 最坏情况:数组逆序,时间复杂度 O(n2);
  • 平均情况:O(n2)。

选择排序的时间复杂度始终是 O(n2),这是因为它的内层循环必须完整遍历未排序区间,无法像冒泡排序那样提前终止。

2. 空间复杂度

选择排序是原地排序算法 (In-place Sort),仅使用了常数级的临时变量(如 tempminIndex),空间复杂度为 O(1)。

3. 稳定性

选择排序是不稳定排序 。例如数组 [2, 3, 2, 1],第一轮找到最小值 1,与第一个 2 交换后,原数组中两个 2 的相对位置被改变(变为 [1, 3, 2, 2]

四、选择排序的优化:双向选择排序

基础选择排序每轮只找最小值,优化思路是每轮同时找最小值和最大值,减少循环次数,这就是 "双向选择排序"(也叫 "鸡尾酒排序" 简化版)

cs 复制代码
// 双向选择排序(同时找最小和最大值)
void bidirectionalSelectionSort(int arr[], int n) {
    int left = 0;          // 未排序区间左边界
    int right = n - 1;     // 未排序区间右边界
    
    while (left < right) {
        int minIndex = left;
        int maxIndex = right;
        
        // 遍历未排序区间,同时找最小和最大值下标
        for (int i = left; i <= right; i++) {
            if (arr[i] < arr[minIndex]) {
                minIndex = i;
            }
            if (arr[i] > arr[maxIndex]) {
                maxIndex = i;
            }
        }
        
        // 交换最小值到左边界
        if (minIndex != left) {
            swap(&arr[left], &arr[minIndex]);
        }
        
        // 注意:如果最大值在左边界(刚被交换走),需要更新maxIndex
        if (maxIndex == left) {
            maxIndex = minIndex;
        }
        
        // 交换最大值到右边界
        if (maxIndex != right) {
            swap(&arr[right], &arr[maxIndex]);
        }
        
        // 缩小未排序区间
        left++;
        right--;
    }
}

优化说明

双向选择排序每轮处理两个元素(最小值放左、最大值放右),循环次数减少约一半,但时间复杂度仍为 O(n2)(仅减少常数项,不改变阶数),适合数据量较大的场景。

五、选择排序的适用场景

选择排序的优势是交换次数少(最多 n−1 次交换),因此适合:

  1. 数据量小的场景(如嵌入式系统、单片机);
  2. 交换成本远高于比较成本的场景(如磁盘数据排序);
  3. 编程新手理解排序逻辑的入门场景。

总结

  1. 选择排序的核心是遍历找最值,交换到指定位置,将数组分为已排序 / 未排序区间;
  2. 基础选择排序的 C 语言实现简单,时间复杂度固定为 O(n2),空间复杂度 O(1),是不稳定排序;
  3. 双向选择排序可减少循环次数,但未改变时间复杂度阶数,适合对交换次数敏感的场景。

选择排序虽然效率不高,但它的逻辑简单、代码易实现,是理解排序算法 "分区间、找规律" 思想的绝佳入门案例。掌握选择排序后,再学习冒泡、插入排序等算法,会更容易理解不同排序思路的差异

相关推荐
qq_417695051 小时前
基于C++的区块链实现
开发语言·c++·算法
We་ct1 小时前
LeetCode 74. 搜索二维矩阵:两种高效解题思路
前端·算法·leetcode·矩阵·typescript·二分查找
2401_894241921 小时前
基于C++的反射机制探索
开发语言·c++·算法
cui_ruicheng2 小时前
C++ 数据结构进阶:unordered_map 与 unordered_set源码分析与实现
数据结构·c++·算法·哈希算法
C蔡博士2 小时前
最小生成树(MST)详解:定义、算法与核心性质
算法·贪心算法·图论·时间复杂度
sxtyjty2 小时前
AtCoder Beginner Contest 450 G题题解
数学·算法·期望
ccLianLian2 小时前
数论·快速幂和逆元
数据结构·算法
没头脑的男大2 小时前
华为题目152乘积最大子数组
算法·华为
牢姐与蒯2 小时前
vector的实现
数据结构