排序算法详解:冒泡、选择与插入排序

51 阅读4分钟

1. 冒泡排序算法

基本概念与排序原理

冒泡排序作为最基础的排序算法,其核心思想是通过相邻元素的反复比较和交换,使得较大元素逐步"浮"到数组末端。下面我们以序列 [8, 7, 6, 3, 1] 的升序排列为例,详细解析其执行过程。

排序过程逐步分析

第一轮排序

  • 8与7比较:8>7,交换 → [7, 8, 6, 3, 1]
  • 8与6比较:8>6,交换 → [7, 6, 8, 3, 1]
  • 8与3比较:8>3,交换 → [7, 6, 3, 8, 1]
  • 8与1比较:8>1,交换 → [7, 6, 3, 1, 8]

第二轮排序

  • 7与6比较:7>6,交换 → [6, 7, 3, 1, 8]
  • 7与3比较:7>3,交换 → [6, 3, 7, 1, 8]
  • 7与1比较:7>1,交换 → [6, 3, 1, 7, 8]

后续轮次依此类推,直到完全有序。

算法复杂度分析

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:稳定排序算法

代码实现

c

#include <stdio.h>

void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换相邻元素
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

int main() {
    int n;
    int arr[10];
    
    printf("请输入元素个数(最多10个): ");
    scanf("%d", &n);
    
    printf("请输入%d个整数: ", n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    
    bubbleSort(arr, n);
    
    printf("排序结果: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

2. 选择排序算法

基本概念与排序原理

选择排序的核心思想是:在未排序序列中反复寻找最小元素,将其存放到已排序序列的末尾。该算法将数组分为已排序和未排序两部分,每次从未排序部分选择最小元素进行交换。

排序过程逐步分析

以序列 [8, 7, 6, 3, 1] 为例:

第一轮:找到最小值1,与首位8交换 → [1, 7, 6, 3, 8]
第二轮:在剩余部分找到最小值3,与第二位7交换 → [1, 3, 6, 7, 8]
第三轮:找到最小值6,位置正确 → [1, 3, 6, 7, 8]
第四轮:找到最小值7,位置正确 → [1, 3, 6, 7, 8]

算法复杂度分析

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定排序算法

代码实现

c

#include <stdio.h>

void selectionSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIndex = i;
        int minValue = arr[i];
        
        // 在未排序部分寻找最小值
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < minValue) {
                minValue = arr[j];
                minIndex = j;
            }
        }
        
        // 将最小值交换到已排序序列末尾
        if (minIndex != i) {
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

int main() {
    int n;
    int arr[10];
    
    printf("请输入元素个数(最多10个): ");
    scanf("%d", &n);
    
    printf("请输入%d个整数: ", n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    
    selectionSort(arr, n);
    
    printf("排序结果: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

3. 插入排序算法

基本概念与排序原理

插入排序采用类似整理扑克牌的思路:将每个新元素插入到已排序部分的适当位置。对于每个待排序元素,从后向前扫描已排序序列,找到相应位置并插入。

排序过程形象比喻

假设手中有 [1, 3, 4, 7, 8] 这些牌,新拿到数字6:

  • 与8比较:6<8,继续向前
  • 与7比较:6<7,继续向前
  • 与4比较:6>4,找到插入位置
    最终序列变为:[1, 3, 4, 6, 7, 8]

排序过程分析

以序列 [8, 7, 6, 3, 1] 为例:

  • 第一轮:7插入到8之前 → [7, 8, 6, 3, 1]
  • 第二轮:6插入到7之前 → [6, 7, 8, 3, 1]
  • 第三轮:3插入到6之前 → [3, 6, 7, 8, 1]
  • 第四轮:1插入到3之前 → [1, 3, 6, 7, 8]

算法复杂度分析

  • 时间复杂度:O(n²),最优情况下为O(n)
  • 空间复杂度:O(1)
  • 稳定性:稳定排序算法

代码实现

c

#include <stdio.h>

void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int current = arr[i];  // 当前待插入元素
        int j = i - 1;
        
        // 将比当前元素大的元素向后移动
        while (j >= 0 && arr[j] > current) {
            arr[j + 1] = arr[j];
            j--;
        }
        
        // 插入当前元素到正确位置
        arr[j + 1] = current;
    }
}

int main() {
    int n;
    int arr[10];
    
    printf("请输入元素个数(最多10个): ");
    scanf("%d", &n);
    
    printf("请输入%d个整数: ", n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    
    insertionSort(arr, n);
    
    printf("排序结果: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

算法对比总结

排序算法平均时间复杂度最优情况最差情况空间复杂度稳定性适用场景
冒泡排序O(n²)O(n)O(n²)O(1)稳定小规模数据、教学用途
选择排序O(n²)O(n²)O(n²)O(1)不稳定小规模数据、交换次数少
插入排序O(n²)O(n)O(n²)O(1)稳定小规模或基本有序数据

这三种基础排序算法虽然时间复杂度较高,但作为算法学习的入门内容,它们能够帮助我们深入理解排序的基本原理和算法设计思想,为学习更高效的排序算法奠定坚实基础。