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

算法:插入排序

插入排序(直接插入排序)

是一种基于“插入”的排序

思路

它的核心思想是把数组分成两部分:一部分是有序区,另一部分是乱序区也就是待排序区。
每次从未排序部分“取出”一个元素,插入到前半部分合适的位置,使得前半部分仍然有序。重复直到所有元素都被插入,排序完成。
举一个例子:
假设数组是:
A = [3, 1, 2, 8, 7]
第 1 步:只有第一个元素 3,它本身就是有序的。
有序区:[3] | 未排序区:[1, 2, 8, 7]
第 2 步:取出 1,插入到前面有序区 [3] 里。
比较 1 和 3,发现 1 < 3,于是把 1 插到 3 前面。
结果:[1, 3] | [2, 8, 7]
第 3 步:取出 2,插入到 [1, 3] 中。
比较 2 和 3,发现 2 < 3,把 3 向后挪,再把 2 插入。
结果:[1, 2, 3] | [8, 7]
第 4 步:取出 8,插入到 [1, 2, 3] 中。
发现 8 比 3 大,直接放到最后。
结果:[1, 2, 3, 8] | [7]
第 5 步:取出 7,插入到 [1, 2, 3, 8] 中。
比较 7 和 8,发现 7 < 8,于是 8 往后挪,把 7 插进去。
结果:[1, 2, 3, 7, 8]
我们很容易就可以发现这个插入排序的过程实际上就像打扑克时,让手中的牌排好的过程。
也很容易发现:
第i趟排序,插入第i个数x,即在有序区中找到x的位置,把该位置空出来,把x插入。
就是把每一个数放到它该处在的有序区的位置,其中第一个数是不用排的,直接从第二个数开始排即可。

代码

思路很简单,重点在于代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <unordered_map>
#include <limits.h>
#include <queue>
#include <string.h>
#include <stack>
using namespace std;int main()
{//ios::sync_with_stdio(0),cin.tie(0),cout.tie(int n;int a[105];//初始序列 cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}//插入排序 for(int i=2;i<=n;i++){//第一个位置不用排,从第二个位置排即可。int k=0;int x=a[i];for(int j=1;j<=i-1;j++){if(a[j]>=a[i]){k=j;break;}} if(k!=0){for(int j=i-1;j>=k;j--){a[j+1]=a[j];}a[k]=x;}}//输出排序后的序列 for(int i=1;i<=n;i++){cout<<a[i]<<" ";} return 0;} 

这里我们是严格的按照:
第i趟排序,插入第i个数x,即
1.在有序区中找到x的位置,
2.把该位置空出来,
3.把x插入。
一步步的写的,实际上我们可以边比较边移动

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <unordered_map>
#include <limits.h>
#include <queue>
#include <string.h>
#include <stack>
using namespace std;int main()
{//ios::sync_with_stdio(0),cin.tie(0),cout.tie(int n;int a[105];//初始序列 cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}//插入排序 for(int i=2;i<=n;i++){//第一个位置不用排,从第二个位置排即可。int k=0;int x=a[i];int j;for(j=i;j>1;j--){if(a[j-1]>x){a[j]=a[j-1];}else{break;}} a[j]=x;}//输出排序后的序列 for(int i=1;i<=n;i++){cout<<a[i]<<" ";} return 0;} 

用vector+从0下标开始更合适

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <unordered_map>
#include <limits.h>
#include <queue>
#include <string.h>
#include <stack>
using namespace std;
vector<int> a(105);
int main()
{//ios::sync_with_stdio(0),cin.tie(0),cout.tie(int n;//初始序列 cin>>n;for(int i=0;i<n;i++){cin>>a[i];}//插入排序 for(int i=1;i<n;i++){//第一个位置不用排,从第二个位置排即可。int k=0;int x=a[i];int j;for(j=i;j>0;j--){if(a[j-1]>x){a[j]=a[j-1];}else{break;}} a[j]=x;}//输出排序后的序列 for(int i=0;i<n;i++){cout<<a[i]<<" ";} return 0;
} 

时间复杂度

由代码很容易看出来,时间复杂是O(n^2)
插入排序不适合对大量数据进行排序应用,但排序数量级小于千时插入排序的效率还不错,可以考虑使用。插入排序在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序(通常为8个或以下)。

就地性

因为只用一个数组,所以是就地排序

稳定性

稳定性是指不改变数组数值的相对位置

        if(a[j-1]>x){a[j]=a[j-1];}else{break;}

只有在>x的时候才移动插入,可以保证稳定性。
因此插入排序是稳定的

内部排序

因为所有待排序记录可以⼀次载⼊内存时,因此它是内部排序。
只有归并排序是外部排序。

优化

插入排序的时间复杂度是O(n^2)是不高的,为了提升性能,有下面几类优化

折半插入排序

在普通的插入排序中,我们从后往前一位一位的比较,找到插入的位置,需要进行O(n)次比较。
但前半部分也就是有序区是有序的,因此我们可以在这里采用一个二分查找来实现比较,而折半插入排序就是这样的。
思路:
第 i 次插入时,在 [0…i-1] 的有序区里,用二分查找找到 key 的插入位置。
然后再把比 key 大的元素整体往后挪一位。
因为采用了二分比较次数由O(n)变到了O(logn)
但是总体的移动次数还是不变的是O(n)
因此总体的时间复杂度还是O(n^2)
原来是O(n)(O(n)+O(n))
现在是O(n)
(O(logn)+O(n))
性能提升有限,只有在比较比移动大的多的时候才有优势
尽管性能有限,但我们也要理解这种思路,对我们的以后学习有启发。

二路插入排序(基于循环数组的优化)

二路插入排序是在折半插入排序的基础上进行改进的。
它对折半插入排序的移动次数无法优化的问题进行了改进。
二路插入排序利用一个循环数组来存放排序好的序列,
插入时如果 key 比当前有序序列的最小值还小,就往前插。
如果 key 比最大值还大,就往后插。这样移动的元素不超过一半。
这就比之前移动所以的元素要更快。
原来的折半插入是:
查找插入位置:O(log n) (二分)
移动元素:是 O(n)
现在二路插入排序把移动元素:O(n) → O(n/2)
时间复杂度即变成O(n)*(O(logn)+O(n/2))
变得更快了,但它仍是O(n^2)的数量级。
重点在于思路掌握

希尔排序(缩小增量排序)

http://www.dtcms.com/a/360681.html

相关文章:

  • LeetCode 刷题【58. 最后一个单词的长度、59. 螺旋矩阵 II】
  • 【开题答辩全过程】以 基于SSM的拾光咖啡厅管理系统的设计与实现为例,包含答辩的问题和答案
  • Introduction to GIS —— Chapter 4(Raster Data Model)
  • 批量修改用户密码的命令chpasswd
  • FTP - 学习/实践
  • JPEG XS概述
  • 草图大师SketchUp 2025下载安装教程与胚子库插件包安装for SketchUp 2025安装教程
  • 【AI智能体】LLM记账智能体+MCP服务-实现步骤与效果展示
  • 网络流量分析——使用Wireshark进行分析
  • SW - 用装配图的方式组合多个子零件然后转换成为零件,可维护性好
  • DRF快速构建RESTful API指南
  • 告别强化学习?GEPA:用“反思性提示词进化”实现超越的新范式
  • 【机器学习】-torch相关知识01
  • shell 命令拓展二——流程控制
  • springboot项目启动时打印maven打包时间
  • 华秋DFM检查PCB设计缺陷、一键导出Gerber、BOM、坐标文件
  • 平面椭圆转化为三阶Bezier曲线的方法
  • 目标检测算法YOLOv4详解
  • cloudflare 负载均衡器介绍
  • 用 MATLAB 实现遗传算法求解一元函数极值:从代码到实践
  • JS实现默认显示部分文字点击按钮显示全部内容
  • 使用组合子构建抽象语法树
  • 24数学建模国赛C
  • Linux性能调试工具之ftrace
  • 【开题答辩全过程】以 基于Java的城市公交查询系统设计与实现为例,包含答辩的问题和答案
  • 元宇宙与旅游产业:虚实融合的文旅新体验
  • 【代码随想录day 21】 力扣 216.组合总和III
  • 【代码随想录day 22】 力扣 39. 组合总和
  • 2025年跨领域职业发展证书选择指南
  • 设计模式:外观模式(Facade Pattern)