rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 选择排序

  • 算法定义
  • 算法思路
  • 算法复杂度
  • 优点与缺点
  • 应用场景
  • 各版本优化说明
  • 代码

选择排序

算法定义

选择排序(Selection Sort)是一种简单直观的排序算法,它的核心思想是每次从待排序的元素中选出最小(或最大)的元素,将其放置在已排序序列的末尾,直到所有元素都排序完毕。

算法思路

选择排序的步骤非常直接:

  1. 从待排序数组的起始位置开始,找出整个数组中的最小元素。
  2. 将最小元素与数组的起始位置元素交换。
  3. 排除起始位置外的剩余元素(待排序部分),重复步骤 1 和 2,直到所有元素都被排序。

简单来说,就是通过不断选择剩余元素中的最小值来构建有序序列。

算法复杂度

  • 最好时间复杂度:O (n²) - 即使数组已经有序,仍需遍历所有元素寻找最小值
  • 最坏时间复杂度:O (n²) - 数组逆序时,比较和交换次数与最好情况相同
  • 平均时间复杂度:O(n²)
  • 空间复杂度:O (1) - 只需要一个临时变量用于交换元素,属于原地排序

优点与缺点

  • 优点:
    • 实现简单,逻辑清晰,易于理解和编码
    • 原地排序,空间复杂度低(O (1))
    • 交换次数少(最多 n-1 次),适合交换成本高的场景
  • 缺点:
    • 时间复杂度高(O (n²)),不适合大规模数据排序
    • 不稳定的排序算法(相等元素的相对位置可能改变)
    • 无论输入数据是否有序,时间复杂度都固定为 O (n²)

应用场景

  • 小规模数据的排序场景
  • 对内存使用限制严格的环境(只需要常数级额外空间)
  • 教学场景(理解简单排序算法的基本思想)
  • 元素交换成本较高,但比较成本较低的场景(选择排序交换次数少)

各版本优化说明

  1. 基本版:
    • 最经典的实现方式,每次从剩余元素中选择最小值
    • 逻辑简单,但效率较低,适合理解选择排序的基本原理
  2. 双向选择排序:
    • 每次遍历同时寻找最小值和最大值
    • 将最小值放到左侧,最大值放到右侧,减少一半的外循环次数
    • 对于大规模数组,实际运行效率比基本版提高约一倍
  3. 优化比较次数版本:
    • 在内层循环中一次比较两个相邻元素,减少比较次数
    • 先找出两个元素中的较小者,再与当前最小值比较
    • 理论上可减少约一半的比较操作

选择排序虽然时间复杂度较高,但由于其实现简单、空间开销小,在一些对性能要求不高的小规模数据排序场景中仍有应用价值。与冒泡排序相比,选择排序的交换次数更少,在交换成本较高的场景下表现更优。

代码

import java.util.Arrays;

public class SelectionSort {
    
    // 1. 基本版选择排序(升序,每次选最小值)
    public static void selectionSortBasic(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        
        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) {
                swap(arr, i, minIndex);
            }
        }
    }
    
    // 2. 优化版1:双向选择排序(同时找最小值和最大值)
    public static void selectionSortBidirectional(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        
        int left = 0;
        int right = arr.length - 1;
        
        while (left < right) {
            int minIndex = left;  // 最小值索引
            int maxIndex = right; // 最大值索引
            
            // 在[left, 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, minIndex);
            }
            
            // 处理最大值索引可能被交换的情况
            if (maxIndex == left) {
                maxIndex = minIndex;
            }
            
            // 将最大值交换到右侧
            if (maxIndex != right) {
                swap(arr, right, maxIndex);
            }
            
            left++;
            right--;
        }
    }
    
    // 3. 优化版2:减少比较次数的选择排序
    public static void selectionSortOptimizedCompare(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }
        
        int n = arr.length;
        for (int i = 0; i < n - 1; i++) {
            int minIndex = i;
            
            // 在内层循环中同时处理两个元素的比较
            for (int j = i + 1; j < n; j += 2) {
                // 处理最后一个可能的奇数元素
                if (j + 1 > n - 1) {
                    if (arr[j] < arr[minIndex]) {
                        minIndex = j;
                    }
                    break;
                }
                
                // 比较相邻的两个元素,先找出较小的那个
                int smaller = (arr[j] < arr[j + 1]) ? j : j + 1;
                // 再与当前最小值比较
                if (arr[smaller] < arr[minIndex]) {
                    minIndex = smaller;
                }
            }
            
            if (minIndex != i) {
                swap(arr, i, minIndex);
            }
        }
    }
    
    // 交换数组中两个元素
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
    public static void main(String[] args) {
        int[] arr = {64, 34, 25, 12, 22, 11, 90, 5};
        int[] arr1 = Arrays.copyOf(arr, arr.length);
        int[] arr2 = Arrays.copyOf(arr, arr.length);
        int[] arr3 = Arrays.copyOf(arr, arr.length);
        
        selectionSortBasic(arr1);
        System.out.println("基本版: " + Arrays.toString(arr1));
        
        selectionSortBidirectional(arr2);
        System.out.println("双向选择排序: " + Arrays.toString(arr2));
        
        selectionSortOptimizedCompare(arr3);
        System.out.println("优化比较次数: " + Arrays.toString(arr3));
    }
}
最近更新:: 2025/9/8 23:27
Contributors: luokaiwen