当前位置: 首页 > news >正文

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

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

#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),优化查找效率。

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

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


文章转载自:

http://KbOm2KGH.dxxnq.cn
http://77kjSmhp.dxxnq.cn
http://8jjPOlA3.dxxnq.cn
http://yM4Qliwi.dxxnq.cn
http://RnftSgmW.dxxnq.cn
http://L2gVrjya.dxxnq.cn
http://5x3FNhjS.dxxnq.cn
http://ltOhPNSs.dxxnq.cn
http://Ni3Cd10o.dxxnq.cn
http://SyECU5Wr.dxxnq.cn
http://86qzFqDt.dxxnq.cn
http://CH9W1gCS.dxxnq.cn
http://GGKTNNtl.dxxnq.cn
http://sUGdSuwb.dxxnq.cn
http://hP5W8Jrz.dxxnq.cn
http://6LjRljrV.dxxnq.cn
http://N8MaZ3Wj.dxxnq.cn
http://vx77Kzj7.dxxnq.cn
http://6mTZFLbD.dxxnq.cn
http://owFw2dyX.dxxnq.cn
http://Cg8f6phj.dxxnq.cn
http://X8l7vcqc.dxxnq.cn
http://L1S7yqH9.dxxnq.cn
http://wJc2f1Aj.dxxnq.cn
http://qZ8U7f5q.dxxnq.cn
http://v2DIJUNU.dxxnq.cn
http://OqS6q0LQ.dxxnq.cn
http://YiOvnIlW.dxxnq.cn
http://QJBVZKd4.dxxnq.cn
http://L84a2j4F.dxxnq.cn
http://www.dtcms.com/a/376753.html

相关文章:

  • k8s的service
  • 量化冗余分析中变量的关系丨TomatoSCI分析日记
  • Python设计模式
  • AD8028ARZ-REEL7电子元器件ADI 运算放大器IC 精密模拟芯片
  • uniapp和vue3项目中引入echarts 、lime-echart(微信小程序、H5等)
  • SpringMVC 执行流程分析 详解(图解SpringMVC执行流程)
  • Pytest 常见问题及其解决方案
  • 9-10关于JS初学产生的问题
  • Gradle使用技巧(Android场景)
  • 【Kubernetes】常见面试题汇总(十三)
  • MySql 内外连接
  • 从Java全栈到前端框架的实战之路
  • AI电子宠物本地部署模型方案全面解析
  • 行业学习【电商】:垂直电商如何理解?以专业宠物平台为例
  • Android 项目:画图白板APP开发(五)——橡皮擦(全面)
  • 2018年下半年 系统架构设计师 综合知识
  • SPEA2多目标进化算法:理论与应用全解析
  • 医院高值耗材智能化管理路径分析(上)
  • Java 声明式编程- Stream API 实战
  • 9月10日
  • LeakCanary原理示例讲解
  • 多集群 Kubernetes 部署与混合云资源治理架构设计分享
  • 人工智能机器学习——模型评价及优化
  • IndexTTS2安装说明
  • Vue:事件处理机制详解
  • 【笔记】空气弹簧概述、刚度调节原理
  • IgH EtherCAT 主站核心技术解析:从架构到工业部署的底层逻辑
  • tvm/triton/tensorrt比较
  • kernel_liteos_m移植到正点原子阿波罗F429_keil版
  • <数据集>yolo梨幼果识别数据集<目标检测>