排序---插入排序(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),但其在小规模数据场景中表现优异,且常作为复杂排序算法的优化补充。

相关推荐
Boop_wu3 小时前
[数据结构] 队列 (Queue)
java·jvm·算法
奔跑吧邓邓子3 小时前
【C++实战⑦】C++函数实战:从基础到项目应用
c++·实战·函数
HMBBLOVEPDX3 小时前
C++(静态函数)
开发语言·c++
hn小菜鸡3 小时前
LeetCode 3643.垂直翻转子矩阵
算法·leetcode·矩阵
2301_770373733 小时前
数据结构之跳表
数据结构
散1123 小时前
01数据结构-初探动态规划
数据结构·动态规划
张晓~183399481213 小时前
短视频矩阵源码-视频剪辑+AI智能体开发接入技术分享
c语言·c++·人工智能·矩阵·c#·php·音视频
ゞ 正在缓冲99%…4 小时前
leetcode101.对称二叉树
算法
YuTaoShao4 小时前
【LeetCode 每日一题】3000. 对角线最长的矩形的面积
算法·leetcode·职场和发展