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

直接插入排序算法:可视化讲解与C语言实现

目录

介绍

算法思想

算法流程图

算法可视化演示

​编辑算法步骤详解

算法特点

时间复杂度分析

空间复杂度分析

稳定性分析

关键特性总结

C语言实现


介绍

        直接插入排序(Straight Insertion Sort)是一种简单直观的排序算法。它的工作原理类似于我们打扑克牌时对手中的牌进行排序。

算法思想

        直接插入排序是一种简单直观的排序算法,核心思想是:将待排序元素插入到已排序序列的适当位置。类似于整理扑克牌的过程:

  1. 从第一个元素开始,该元素可以认为已经被排序。
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。
  4. 重复步骤3,直到找到已排序的元素小于等于新元素的位置。
  5. 将新元素插入到该位置后。
  6. 重复步骤2~5,直到所有元素都排序完毕。

算法流程图

算法可视化演示

        接下来将根据算法流程图来讲解,以数组arr={2,0,6,8,6,7,7,9,1,3}共10个元素,进行直接插入排序的算法可视化演示。其中数组arr;数组长度n=10;下标i=0,1,...,8,9;key用来存储当前未排序元素的原始值;j=i-1,...,0用来表示已排序区的元素的下标。

算法步骤详解

        数组arr={2,0,6,8,6,7,7,9,1,3},共10个元素,下图展示了其未排序前的状态。 

        对于arr[0] = 2,因为其是第一个元素,直接标记成已排序,第一个不用排序,这也是流程图中从i=1开始,而不是i=0开始的原因。区分已排序区和未排序区两个概念有利于我们理解排序的过程。做个类比:已排序区相当于我们手中已经抓到的牌,未排序区指还未摸的牌。(注:这里假设摸到手里的牌都会按顺序插入排列,不会随意放置)。接下来就是遍历未排序区的元素,即arr[1]、arr[2]、,...,arr[9]进行排序。

        对于arr[1] = 0,首先令key = arr[1],将arr[1]位置原本的值保存下来。因为排序中可能涉及到元素移动,原位置的元素就有可能被覆盖,从而丢失。

        接下来就是要确定key插入的位置,可以想象的是,如果数组排序完之后按照升序排列(即小的数在下标小的位置,大的数在下标大的位置),那么key插入的位置应该满足以下条件:

左边的数<= key <= 右边的数。至于这个"==",就涉及到数组中存在相同的元素时的处理情况。直接插入排序算法,不改变原始数组中相同元素的相对位置。即若排序前 a==a‘ ,a在a'左侧,则排序后a还在a'左侧,下面示例中会有涉及。

根据流程图,此时i=1, j = i - 1 = 0  key只需要跟已排序区中的arr[0]对应的2比较即可

2 > 0,所以2右移,就是流程图中的arr[j+1] = arr[j]这行代码实现的右移。

j = j - 1 = 0 -1 = -1 < 0 所以针对当前未排序元素key的排序结束了

给key放到合适的位置,即代码arr[j+1] = key.此时j=-1,j+1 = 0,即arr[0] = key.

i = 1 即下标1的插入结束,i = i + 1 = 2  < n(10),继续下标2的插入处理。

同理,把key = arr[i] (i=2),即key =6;

j = i -1 (i=2),即j = 1;可见此时已排序区有两个元素(下标1和下标0),即6可能要依次跟下标1对应的2,下标0对应的0进行比较。6需要先跟2比,视结果,再决定是否还要与0比较。

6先与2比较,2 < 6,所以6的位置已经确定了,不用再与下标0对应的0比了。因为有前面的插入排序,可以确定已排序区是升序的。6不小于下标1对应的元素,则一定不小于下标0对应的元素。

直接将6插入即可 i = i + 1 = 2 + 1 = 3 < n(10),所以未排序区还没遍历完,继续。

同理,把key = arr[i] (i=3),即key =8;

j = i -1 (i=3),即j = 2;可见此时已排序区有3个元素(下标2、1、0),即8可能要依次跟下标2对应的6、下标1对应的2,下标0对应的0进行比较。8需要先跟6比,视结果,再决定是否还要与2比较。

8先与6比较,6 < 8,所以8的位置已经确定了,不用再与下标1和下标0比了。因为有前面的插入排序,可以确定已排序区是升序的。8不小于下标2对应的元素,则一定不小于下标1和0对应的元素。

直接将8插入即可 i = i + 1 = 3 + 1 = 4 < n(10),所以未排序区还没遍历完,继续。

同理,把key = arr[i] (i=4),即key =6;

j = i -1 (i=4),即j = 3;可见此时已排序区有4个元素(下标3、2、1、0),即6可能要依次跟下标3、2、1、0对应的元素进行比较。6需要先跟下标3对应的元素比,视结果,再决定是否还要继续比较。

6先与8比较,8  > 6,所以6的位置还不确定,将8先右移1位,6继续和下标2对应的元素比较。

6先再与下标2对应的6比较,arr[2] = 6, 不大于6,所以当前要排序的这个6的位置已经确定了,不用再与下标1和下标0比了。因为有前面的插入排序,可以确定已排序区是升序的。6不小于下标2对应的元素,则一定不小于下标1和0对应的元素。此次插入,体现了直接插入排序算法对相同元素情况时的处理。即,直接插入排序不会改变数组中相同元素的相对位置。

将6插入,然后继续下面的排序。

接下来的排序就比较相似了,都遵循流程图。最终排序完成后的数组如下:

算法特点

时间复杂度分析

情况时间复杂度说明
最好情况O(n)当输入数组已经有序时,每次插入只需比较一次(因为新元素大于已排序序列的最后一个元素),不需要移动。总共需要比较n-1次,因此时间复杂度为O(n)。
最坏情况O(n²)当输入数组完全逆序时,每次插入都需要将新元素与已排序序列中的所有元素比较并移动。对于第i个元素(从1开始),需要比较和移动i次(即从i-1到0)。总比较和移动次数为:1 + 2 + ... + (n-1) = n(n-1)/2,因此时间复杂度为O(n²)。
平均情况O(n²)考虑到随机排列的数组,每个元素平均需要比较和移动的次数约为i/2(i为当前插入的位置),所以总次数约为n²/4,因此时间复杂度为O(n²)。

空间复杂度分析

复杂度说明
O(1)仅需常数级别额外空间(用于key和索引变量i,j),是原地排序算法

稳定性分析

特性说明
稳定排序当遇到相等元素时(arr[j] == key),由于条件arr[j] > key不成立,循环停止,新元素插入在相等元素之后,保持了原有顺序

关键特性总结

维度特性
时间复杂度依赖输入数据的有序程度
空间效率最优的原地排序算法之一
稳定性保持相等元素相对位置
适用场景小数据集/部分有序数据

C语言实现

#include <stdio.h>// 直接插入排序函数
void insertionSort(int arr[], int n) 
{for (int i = 1; i < n; i++) {  // 从第二个元素开始int key = arr[i];          // 当前待插入元素int j = i - 1;// 将大于key的元素后移while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];  // 元素后移j--;}arr[j + 1] = key;  // 插入到正确位置}
}// 打印数组
void printArray(int arr[], int size) 
{for (int i = 0; i < size; i++) {printf("%d ", arr[i]);}printf("\n");
}int main() 
{int arr[] = {2,0,6,8,6,7,7,9,1,3};int n = sizeof(arr) / sizeof(arr[0]);printf("原始数组: ");printArray(arr, n);insertionSort(arr, n);printf("排序后数组: ");printArray(arr, n);return 0;
}

程序输出

http://www.lryc.cn/news/614891.html

相关文章:

  • Android MediaMetadataRetriever取视频封面,Kotlin(1)
  • 记一次奇异的bug
  • 自动化一键部署 LNMP 环境
  • 【n8n教程笔记——工作流Workflow】文本课程(第二阶段)——5 自动化业务工作流——0 用例 (Use case)
  • 五、RuoYi-Cloud-Plus 前端项目部署以及如何改后端请求地址。
  • 线上排查问题的一般流程是怎么样的?
  • 集成电路学习:什么是RQT图形用户界面工具
  • 搭建商城的关键注意事项:从定位到运营的全链路指南
  • 基于 InfluxDB 的服务器性能监控系统实战(二)
  • 深入解析进程创建与终止机制
  • Linux 信号处理标志sa_flags详解
  • 有限元方法中的数值技术:Cholesky矩阵分解
  • 从零学习three.js官方文档(一)——基本篇
  • 校招秋招春招实习快手在线测评快手测评题库|测评解析和攻略|题库分享
  • 【linux基础】Linux目录和Windows目录的区别
  • 免费开发数字人API
  • Milvus 向量数据库基础操作解析
  • Kubernetes 无法识别你定义的 `CronJob` 资源*逐步解决方案
  • 不足3个细胞怎么做差异分析?
  • 目标检测数据集 - 足球场广告横幅检测数据集下载「包含VOC、COCO、YOLO三种格式」
  • 【Datawhale AI夏令营】从Baseline到SOTA:深度剖析金融问答RAG管道优化之路
  • [CUDA] CUTLASS | `CuTe DSL` 创新
  • 《TypeScript搭建的认知桥梁:游戏化学习应用的深层架构》
  • day22|学习前端ts语言
  • Javaweb - 14.1 - 前端工程化
  • 政府数字化大屏系统 - Flask实现方案
  • 使用LangGraph从零构建多智能体AI系统:实现智能协作的完整指南
  • OpenAI开源大模型 GPT-OSS 开放权重语言模型解析:技术特性、部署应用及产业影响
  • HTML金色流星雨
  • 人工智能技术发展历史演变