排序---插入排序(Insertion Sort)

一、算法核心思想

插入排序(Insertion Sort)是一种直观且简单的排序算法,其核心思想借鉴了日常生活中"整理手牌"的行为------将一组元素分为"已排序"和"未排序"两部分,每次从"未排序"部分取出一个元素,按照大小顺序插入到"已排序"部分的合适位置,直到所有元素都被插入完毕。

这种算法的本质是通过逐步构建有序序列实现排序:初始时,"已排序"部分仅包含第一个元素;之后,从第二个元素开始,每一步都将当前元素与"已排序"部分的元素逐一比较,找到其正确位置并插入,最终使整个序列有序。

二、算法步骤分解

以数组 [5, 2, 4, 6, 1, 3] 为例,详细说明插入排序的执行步骤:

  1. 初始状态

    已排序部分:[5](第一个元素默认有序)

    未排序部分:[2, 4, 6, 1, 3]

  2. 插入第2个元素(2)

    • 比较2与已排序部分的5:2 < 5,需插入到5左侧。
    • 将2存入temp中
    • 移动元素:将5右移一位,数组变为 [5, 5, 4, 6, 1, 3]
    • 插入元素:将2放入空位,数组变为 [2, 5, 4, 6, 1, 3]
    • 此时已排序部分:[2, 5],未排序部分:[4, 6, 1, 3]
  3. 插入第3个元素(4)

    • 比较4与5:4 < 5,继续比较前一个元素2:4 > 2,确定插入位置在2和5之间。
    • 移动元素:将5右移一位,数组变为 [2, 5, 5, 6, 1, 3]
    • 插入元素:将4放入空位,数组变为 [2, 4, 5, 6, 1, 3]
    • 此时已排序部分:[2, 4, 5],未排序部分:[6, 1, 3]
  4. 插入第4个元素(6)

    • 比较6与5:6 > 5,无需移动元素,直接插入到已排序部分末尾。
    • 数组变为 [2, 4, 5, 6, 1, 3]
    • 此时已排序部分:[2, 4, 5, 6],未排序部分:[1, 3]
  5. 插入第5个元素(1)

    • 依次比较1与6、5、4、2:1小于所有元素,需插入到最左侧。
    • 移动元素:将2、4、5、6依次右移一位,数组变为 [2, 2, 4, 5, 6, 3]
    • 插入元素:将1放入空位,数组变为 [1, 2, 4, 5, 6, 3]
    • 此时已排序部分:[1, 2, 4, 5, 6],未排序部分:[3]
  6. 插入第6个元素(3)

    • 依次比较3与6、5、4:3 < 4,继续比较2:3 > 2,确定插入位置在2和4之间。
    • 移动元素:将4、5、6依次右移一位,数组变为 [1, 2, 4, 4, 5, 6]
    • 插入元素:将3放入空位,数组变为 [1, 2, 3, 4, 5, 6]
  7. 结束:所有元素插入完成,数组已排序。

三、算法特性分析
1. 时间复杂度
  • 最好情况 :数组已完全有序。此时每个元素只需与前一个元素比较1次(无需移动),总操作次数为 O(n)O(n)O(n)。
  • 最坏情况 :数组完全逆序。每个元素需要与已排序部分的所有元素比较并移动,总操作次数为 O(n2)O(n^2)O(n2)(求和公式:1+2+...+(n−1)=n(n−1)/21+2+...+(n-1) = n(n-1)/21+2+...+(n−1)=n(n−1)/2)。
  • 平均情况 :对于随机排列的数组,平均比较和移动次数约为 n2/4n^2/4n2/4,时间复杂度为 O(n2)O(n^2)O(n2)。
2. 空间复杂度

插入排序是原地排序(In-place Sort),仅需额外常数级空间(如临时变量存储当前元素),空间复杂度为 (O(1))。

3. 稳定性

插入排序是稳定排序 。当遇到相等元素时,新元素会插入到原有相等元素的右侧,保持其相对顺序不变。例如,对 [3, 2, 2] 排序时,两个2的位置不会交换。

4. 适用场景
  • 小规模数据(如 n≤100n \leq 100n≤100):简单直接,无需额外空间。
  • 接近有序的数据:此时比较和移动次数少,效率接近 (O(n))。
  • 作为复杂算法的子过程:例如归并排序、快速排序中,对小规模子数组使用插入排序可优化性能。
四、C/C++实现代码

以下是插入排序的C++实现,包含完整的排序函数、测试用例及步骤打印:

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

// 插入排序函数:对数组arr进行升序排序
void insertionSort(vector<int>& arr) {
    int n = arr.size();
    // 若数组为空或仅有一个元素,直接返回
    if (n <= 1) return;

    // 外层循环:遍历未排序部分(从第2个元素开始)
    for (int i = 1; i < n; i++) {
        int current = arr[i];  // 记录当前待插入元素
        int j = i - 1;         // 已排序部分的末尾索引

        // 内层循环:在已排序部分中找到插入位置
        // 若已排序元素大于current,将其右移一位
        while (j >= 0 && arr[j] > current) {
            arr[j + 1] = arr[j];  // 元素右移
            j--;
        }
        // 将current插入到正确位置
        arr[j + 1] = current;

        // 打印当前步骤的排序结果(可选,用于调试)
        cout << "第" << i << "轮插入后:";
        for (int num : arr) {
            cout << num << " ";
        }
        cout << endl;
    }
}

int main() {
    // 测试用例
    vector<int> arr = {5, 2, 4, 6, 1, 3};
    
    cout << "排序前的数组:";
    for (int num : arr) {
        cout << num << " ";
    }
    cout << endl << endl;

    // 执行插入排序
    insertionSort(arr);

    cout << endl << "排序后的数组:";
    for (int num : arr) {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}
代码说明:
  1. 函数设计insertionSort 接收一个vector引用,直接在原数组上修改(原地排序)。
  2. 外层循环i 从1开始,遍历未排序部分的每个元素(arr[1]arr[n-1])。
  3. 内层循环ji-1 开始,向前遍历已排序部分,若 arr[j] > current 则右移元素,直到找到 current 的插入位置(j+1)。
  4. 插入操作 :将 current 放入 j+1 位置,完成一次插入。
五、优化思路:二分插入排序

插入排序的主要耗时操作是在已排序部分查找插入位置 (线性扫描)。若改用二分查找 定位插入位置,可将比较次数从 O(n)O(n)O(n) 降至 O(log⁡n)O(\log n)O(logn),优化查找效率。

二分插入排序的核心修改是将内层循环的线性查找替换为二分查找,代码示例如下:

cpp 复制代码
void binaryInsertionSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 1; i < n; i++) {
        int current = arr[i];
        int left = 0, right = i - 1;

        // 二分查找插入位置
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (current < arr[mid]) {
                right = mid - 1;  // 目标在左半部分
            } else {
                left = mid + 1;   // 目标在右半部分
            }
        }
        // 找到插入位置left,将元素右移
        for (int j = i - 1; j >= left; j--) {
            arr[j + 1] = arr[j];
        }
        arr[left] = current;  // 插入元素
    }
}

注意 :二分插入排序仅减少比较次数,元素移动次数仍为 O(n2)O(n^2)O(n2),因此时间复杂度仍为 O(n2)O(n^2)O(n2),但实际效率优于普通插入排序(尤其数据量较大时)。


插入排序是一种简单直观的排序算法,其优点是实现简单、空间复杂度低、稳定且对接近有序的数据高效。尽管时间复杂度为 O(n2)O(n^2)O(n2),但其在小规模数据场景中表现优异,且常作为复杂排序算法的优化补充。

相关推荐
小龙报2 分钟前
《C语言疑难点 --- 字符函数和字符串函数专题(上)》
c语言·开发语言·c++·算法·学习方法·业界资讯·visual studio
趙小贞14 分钟前
字符设备驱动开发流程与实战:以 LED 驱动为例
linux·c语言·驱动开发
程序员阿鹏29 分钟前
560.和为k的子数组
数据结构
傻童:CPU31 分钟前
C语言练习题
c语言·开发语言
zbh060437 分钟前
洛谷P5788 【模板】单调栈——单调栈
数据结构·算法
咔咔咔的44 分钟前
3461. 判断操作后字符串中的数字是否相等 I
c++
liulilittle1 小时前
LwIP协议栈MPA多进程架构
服务器·开发语言·网络·c++·架构·lwip·通信
moonsims1 小时前
Z3 Technology-适用于无人机和机器人的 4K 高清摄像机和视频编码器
算法
摘星编程1 小时前
深入浅出 Tokio 源码:掌握 Rust 异步编程的底层逻辑
网络·算法·rust·系统编程·tokio
艾莉丝努力练剑2 小时前
【C++:继承】面向对象编程精要:C++继承机制深度解析与最佳实践
开发语言·c++·人工智能·继承·c++进阶