首页 > 其他分享 >直接插入排序讲解

直接插入排序讲解

时间:2025-01-04 13:29:01浏览次数:3  
标签:11 arr 直接 int 插入排序 13 12 讲解 排序

直接插入排序

定义

直接插入排序是一种简单直观的排序算法,适用于少量数据的排序任务。它的工作原理是将数组分为已排序和未排序两部分,然后将未排序部分的每个元素按顺序插入到已排序部分的适当位置。


算法步骤

  1. 初始化: 将第一个元素视为已排序部分,其余元素为未排序部分。
  2. 遍历未排序部分:
    • 从第二个元素开始,依次取出每个元素。
    • 将该元素插入到前面已排序部分的正确位置。
  3. 插入:
    • 从后向前扫描已排序部分,找到插入位置。
    • 如果扫描到的元素比当前元素大,则将该元素后移一位。
  4. 重复步骤 2 和 3,直到所有元素都插入完成。

复杂度分析

  • 时间复杂度:

    • 最好情况:数组已近乎有序,比较 O ( n ) O(n) O(n) ,移动 O ( 1 ) O(1) O(1) 。总复杂度为 O ( n ) O(n) O(n) 。
    • 最坏情况:数组完全逆序,比较和移动均为 O ( n 2 ) O(n^2) O(n2) 。总复杂度为 O ( n 2 ) O(n^2) O(n2)。
    • 平均情况:比较和移动均为 O ( n 2 ) O(n^2) O(n2) 。
  • 空间复杂度: O ( 1 ) O(1) O(1) ,因为不需要额外的空间。

  • 稳定性: 稳定,因为在插入过程中,不会改变相同元素的相对顺序。


代码示例(C)

以下是直接插入排序的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;

        // 向前扫描已排序部分,寻找插入位置
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];  // 将大于 key 的元素后移
            j--;
        }
        arr[j + 1] = key;  // 插入到正确位置
    }
}

// 打印数组函数
void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// 主函数
int main() {
    int arr[] = {12, 11, 13, 5, 6};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前的数组: ");
    printArray(arr, n);

    insertionSort(arr, n);

    printf("排序后的数组: ");
    printArray(arr, n);

    return 0;
}

代码分析

1. 打印数组模块

函数原型:

void printArray(int arr[], int n);

功能说明:

  • 负责遍历并打印数组的内容。
  • 用于查看数组排序前和排序后的结果。

代码分析:

void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]); // 打印数组的每个元素,之间用空格隔开
    }
    printf("\n"); // 打印换行符,结束当前行
}

输入参数:

  • arr[]: 待打印的数组。
  • n: 数组的长度。

关键点:

  • 使用 for 循环遍历数组,逐个打印元素。
  • 每个元素后面用空格隔开,打印完成后换行。

作用:

  • 帮助调试和验证排序结果。

2. 直接插入排序模块

函数原型:

void insertionSort(int arr[], int n);

功能说明:

  • 对数组进行排序,将数组分为已排序和未排序两部分。
  • 每次从未排序部分取出一个元素,插入到已排序部分的适当位置。

代码分析:

void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {  // 遍历未排序部分,从第二个元素开始
        int key = arr[i];  // 记录当前待插入的元素
        int j = i - 1;     // j 是已排序部分的最后一个元素索引

        // 向前扫描已排序部分,找到 key 应插入的位置
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j]; // 将大于 key 的元素后移一位
            j--;                 // 向前继续比较
        }

        arr[j + 1] = key; // 插入 key 到正确位置
    }
}

输入参数:

  • arr[]: 待排序的数组。
  • n: 数组的长度。

关键点:

  1. 外层循环

    • for (int i = 1; i < n; i++): 遍历数组的未排序部分,从第二个元素开始。
    • 第一个元素默认视为已排序部分,无需处理。
  2. 保存待插入元素

    • int key = arr[i];:保存当前待插入的元素,避免覆盖问题。
  3. 内层循环

    • while (j >= 0 && arr[j] > key)
      • 从后向前扫描已排序部分,找到插入位置。
      • 如果当前元素大于 key,则将其后移一位,为 key 腾出位置。
  4. 插入操作

    • arr[j + 1] = key;:将 key 插入到正确位置。

3. 主函数模块

函数原型:

int main();

功能说明:

  • 定义数组,调用排序和打印函数。
  • 展示排序前和排序后的数组。

代码分析:

int main() {
    int arr[] = {12, 11, 13, 5, 6}; // 定义待排序数组
    int n = sizeof(arr) / sizeof(arr[0]); // 计算数组长度

    printf("排序前的数组: ");
    printArray(arr, n); // 打印排序前的数组

    insertionSort(arr, n); // 调用插入排序函数

    printf("排序后的数组: ");
    printArray(arr, n); // 打印排序后的数组

    return 0; // 程序正常结束
}

关键点:

  1. 数组初始化

    • int arr[] = {12, 11, 13, 5, 6};:定义并初始化待排序数组。
  2. 数组长度计算

    • sizeof(arr) / sizeof(arr[0]): 数组总字节数除以单个元素的字节数,计算数组长度。
  3. 函数调用顺序

    • 调用 printArray 打印排序前数组。
    • 调用 insertionSort 执行排序。
    • 再次调用 printArray 打印排序后的数组。

图示过程

初始状态

数组为 {12, 11, 13, 5, 6},我们把第一个元素 12 看作是初始已排序部分,后面的元素逐个插入到这个已排序部分中。

索引01234
元素12111356

第一轮排序(插入 11

  • 此时 i = 1key = 11j = 0
  • 比较 1112,因为 12 > 11,所以将 12 后移一位(也就是 arr[1] = arr[0]; 这步操作在代码里的体现)。
  • 然后 j 变为 -1,跳出内层循环,把 key(也就是 11)插入到 arr[0] 的位置。
索引01234
元素11121356

第二轮排序(插入 13

  • i = 2key = 13j = 1
  • 比较 1312,发现 12 < 13,此时不需要移动元素,直接将 13 插入到 arr[2] 位置(也就是 arr[j + 1] = key; 的体现)。
索引01234
元素11121356

第三轮排序(插入 5

  • i = 3key = 5j = 2
  • 比较 51313 > 5,将 13 后移一位(arr[3] = arr[2]; ),j 变为 1
  • 再比较 51212 > 5,将 12 后移一位(arr[2] = arr[1]; ),j 变为 0
  • 接着比较 51111 > 5,将 11 后移一位(arr[1] = arr[0]; ),j 变为 -1。
  • 最后把 5 插入到 arr[0] 位置(arr[j + 1] = key; )。
索引01234
元素51112136

第四轮排序(插入 6

  • i = 4key = 6j = 3
  • 比较 61313 > 6,将 13 后移一位(arr[4] = arr[3]; ),j 变为 2
  • 比较 61212 > 6,将 12 后移一位(arr[3] = arr[2]; ),j 变为 1
  • 比较 61111 > 6,将 11 后移一位(arr[2] = arr[1]; ),j 变为 0
  • 比较 655 < 6,此时停止移动元素,将 6 插入到 arr[1] 位置(arr[j + 1] = key; )。
索引01234
元素56111213

最终经过这几轮排序后,数组变为有序的 {5, 6, 11, 12, 13}

运行流程图解

假设输入数组为 {12, 11, 13, 5, 6},排序过程如下:

步骤未排序部分已排序部分当前插入元素结果数组
初始状态{11, 13, 5, 6}{12}11{11, 12, 13, 5, 6}
第 2 次插入{13, 5, 6}{11, 12}13{11, 12, 13, 5, 6}
第 3 次插入{5, 6}{11, 12, 13}5{5, 11, 12, 13, 6}
第 4 次插入{6}{5, 11, 12, 13}6{5, 6, 11, 12, 13}

最终结果

数组从 {12, 11, 13, 5, 6} 排序为 {5, 6, 11, 12, 13}


优缺点

优点:

  • 简单易实现,适合小规模数据。
  • 稳定性好,能保持相同值的元素顺序。

缺点:

  • 对大规模数据效率较低。
  • 最坏情况下需要较多的比较和移动操作。

标签:11,arr,直接,int,插入排序,13,12,讲解,排序
From: https://blog.csdn.net/2302_79730293/article/details/144806151

相关文章

  • 基于微信小程序的二手物品交易平台ssm+论文源码调试讲解
    第4章系统设计一个成功设计的系统在内容上必定是丰富的,在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值,吸引更多的访问者访问系统,以及让来访用户可以花费更多时间停留在系统上,则表明该系统设计得比较专业。4.1设计原则本系统在设计过程中需要依照一定......
  • 故障诊断一区直接写,图卷积+BiGRU-Attention 并行诊断模型
    往期精彩内容:Python-凯斯西储大学(CWRU)轴承数据解读与分类处理基于FFT+CNN-BiGRU-Attention时域、频域特征注意力融合的轴承故障识别模型-CSDN博客基于FFT+CNN-Transformer时域、频域特征融合的轴承故障识别模型-CSDN博客Python轴承故障诊断(11)基于VMD+CNN-Bi......
  • 基于java的SpringBoot/SSM+Vue+uniapp的多媒体素材管理系统的详细设计和实现(源码+lw+
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于java的SpringBoot/SSM+Vue+uniapp的在线政务服务中心的详细设计和实现(源码+lw+部
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 【初阶数据结构与算法】排序算法总结篇(每个小节后面有源码)(直接插入、希尔、选择、堆
    文章目录一、直接插入排序二、希尔排序三、直接选择排序四、堆排序五、冒泡排序六、快速排序七、归并排序八、计数排序九、非递归快速排序十、非递归归并排序本篇内容将从稳定性与复杂度等角度分析排序算法,列出它们的特点、优点以及缺点,希望大家有所收获,如果想更加细......
  • Spark招聘数据可视化分析+推荐算法+薪资预测+爬虫+讲解视频+论文 大数据毕业设计 Hado
    博主介绍:✌全网粉丝10W+,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业项目实战6年之久,选择我们就是选择放心、选择安心毕业✌>......
  • 详解 使用结构体内存布局直接映射
    使用结构体内存布局直接映射数据帧详解在某些固定格式的数据帧解析中,直接将二进制数据帧映射到结构体是一个高效且简洁的方式。这种方法利用结构体的内存布局直接解析数据帧的字段,而无需手动逐字节处理。以下是更详细的分步骤说明及示例:核心思路结构体布局直接映射:将固......
  • HTML5 开关(Toggle Switch)详细讲解
    HTML5开关(ToggleSwitch)详细讲解1.任务概述开关(ToggleSwitch)是一种用于表示二元状态(如开/关)的用户界面控件。用户可以通过点击开关来切换状态,常见于设置选项、开关功能等场景。2.代码结构以下是实现开关控件的完整代码:<!DOCTYPEhtml><htmllang="zh"><head>......
  • WANGEDITOR复制WORD图片直接显示
    要求:开源,免费,技术支持编辑器:wangEditor前端:vue2,vue3,vue-cli,html5后端:java,jsp,springboot,asp.net,php,asp,.netcore,.netmvc,.netform需求:复制粘贴word内容和图片群体:学生,个人用户,外包,自由职业者,中小型网站,博客,场景:数字门户,数字中台,站群,内网,外网,信创......
  • 小白也能懂文本挖掘之词频统计和词云图绘制(附代码讲解)
    一、词频统计和词云图简介 词频统计和词云图绘制是文本分析中的常见任务,它们能够帮助我们快速理解文本中的关键信息和主题。 词频统计是指对文本中出现的各个词汇进行计数,以了解每个词汇在文本中出现的频率。这是文本分析的基础步骤之一,有助于识别文本中的关键信息和主题......