【数据结构排序算法篇】----插入排序【实战演练】

简介: 【数据结构排序算法篇】----插入排序【实战演练】

作为一名对技术充满热情的学习者,我一直以来都深刻地体会到知识的广度和深度。在这个不断演变的数字时代,我远非专家,而是一位不断追求进步的旅行者。通过这篇博客,我想分享我在某个领域的学习经验,与大家共同探讨、共同成长。请大家以开放的心态阅读,相信你们也会在这段知识之旅中找到启示。



前言

继续学习下一个排序算法----插入排序,我们废话不多说,直接进入主题吧


一、什么是插入排序

插入排序是一种简单直观的排序算法,它的工作方式类似于我们整理手中扑克牌的方式。具体操作如下:

  1. 开始时我们左手为空并且桌子上的牌面朝下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌的正确位置,我们从右向左将它与手中已有的牌相比较。
  2. 数组中实现就是从数组的第二个元素开始把待插入的元素复制为一个副本(记作 key)。比较其与之前元素的大小,如果 key 更小,则之前元素向后移动一位。这个过程会一直重复,直到找到了 key 的正确位置,然后将 key 插入到这个位置。
  3. 算法过程逐步将每个元素加入到前面已经排序的序列中,在插入的过程中查找其位置,直到整个数组排序完成。

插入排序的算法时间复杂度为O(n^2),因为它需要比较的次数最坏情况是一个递增数列,但如果输入数组已经是部分排序的话,其性能会比较好。插入排序是稳定排序,也就是说,如果两个相等的元素之间的相对位置在排序前后不会改变。

下面是插入排序的一个简单实现示例(Java):

public class InsertionSort {
    public static void sort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            // 选择第 i 个元素作为待插入元素
            int key = array[i];
            int j = i - 1;
            
            // 将比 key 大的元素向后移动
            while (j >= 0 && array[j] > key) {
                array[j + 1] = array[j];
                j = j - 1;
            }
            // 插入 key 到正确的位置
            array[j + 1] = key;
        }
    }
    public static void main(String[] args) {
        int[] exampleArray = {5, 2, 4, 6, 1, 3};
        sort(exampleArray);
        // 输出排序后的数组
        for (int i : exampleArray) {
            System.out.print(i + " ");
        }
    }
}

这段代码展示了插入排序的基本操作,通过一个外部循环逐步增加排序的范围,然后在每一步通过一个内部循环寻找当前元素的合适位置,并进行插入。

二、实战演练

简单练习

  • 题目:
    假设你有一个整数数组 [29, 10, 14, 37, 13]。使用插入排序算法对该数组进行排序。手写出你排序时数组的状态变化,以及解释每一步的操作。
  • 解答:
    这里是按照插入排序算法,对数组 [29, 10, 14, 37, 13] 进行排序的逐步过程:

初始数组状态:

29, 10, 14, 37, 13

第1轮(考虑元素 10):

  • 10 应该被插入到 29 前面,因为 10 < 29
    变化后数组状态:
10, 29, 14, 37, 13

第2轮(考虑元素 14):

  • 14 应该被插入到 29 前面,找到适当位置后,就是 1029 之间。
    变化后数组状态:
10, 14, 29, 37, 13

第3轮(考虑元素 37):

  • 37 已经比左侧的所有元素都大,所以它应该留在原位。
    变化后数组状态:
10, 14, 29, 37, 13

第4轮(考虑元素 13):

  • 13 应该被插入到 14 前面,位置是 1014 之间。
    变化后数组状态:
10, 13, 14, 29, 37

操作结束,数组已经完全排序。

以上步骤展示了插入排序的核心概念:逐个取出未排序部分的元素,并将它插入到已排序部分的适当位置,直到数组完全排序

复杂练习

  • 题目:
    给定一个整数数组 [34, 8, 64, 51, 32, 21],使用插入排序算法对该数组进行排序,并记录在整个排序过程中进行了多少次比较和多少次移动。
  • 解答:
    我们逐步执行插入排序,同时计数比较和移动次数。

初始数组状态:

34, 8, 64, 51, 32, 21
比较次数 = 0
移动次数 = 0

第1轮(考虑元素 8):

[8, 34], 64, 51, 32, 21
比较次数 = 1 (34 和 8 比较一次)
移动次数 = 1 (34 移动到原来 8 的位置)

第2轮(考虑元素 64):

8, 34, [64], 51, 32, 21
比较次数 = 1 (64 和 34 比较一次)
移动次数 = 0 (64 已经在正确位置,无需移动)

第3轮(考虑元素 51):

8, 34, [51, 64], 32, 21
比较次数 = 2 (51 先和 64 比较,然后和 34 比较)
移动次数 = 1 (64 移动到原来 51 的位置)

第4轮(考虑元素 32):

8, [32, 34, 51, 64], 21
比较次数 = 3 (32 依次与 51、34 和 8 比较)
移动次数 = 3 (34、51 和 64 分别向后移动一位)

第5轮(考虑元素 21):

[8, 21, 32, 34, 51, 64]
比较次数 = 4 (21 依次与 32、34、51 和 8 比较)
移动次数 = 4 (32、34、51、64 向后移动)

操作结束,数组完全排序。

最终排序后的数组:

8, 21, 32, 34, 51, 64

总比较次数和移动次数:

总比较次数 = 1 + 1 + 2 + 3 + 4 = 11
总移动次数 = 1 + 0 + 1 + 3 + 4 = 9

这个例题考察了使用插入排序进行排序同时跟踪了比较和移动的微观操作,这对于理解算法的具体影响和实现细节非常有帮助。这种计数通常用于分析算法效率和它的操作复杂性。

三、经典面试题

  • 题目:

你正在优化一个已经部分排序的巨大数组。该数组的开始部分(前 10%)是杂乱无序的,而余下的部分(后

90%)已经是排序好的。请提出一个优化过程使插入排序在这种特定情况下运行得更快,并解释为什么这个方法会有效。然后手动演示你的方法如何应用到以下数组:

[3, 7, 4, 9, 5, 2, 6, 1, 14, 18, 16, 17, 20, 13, 12, 11, 19, 15, 12, 10, ...](数组中只给出了未排序的前部分和部分已排序的后部分,假设后部分是一个巨大并且完全排序的数组)。

  • 解答:优化过程:
  • 我们可以分两部分来处理数组。首先,对数组前面杂乱的 10% 进行普通的插入排序,将它们整理成有序。然后将这部分有序的数据与后面已经排好序的 90% 部分进行合并。
  • 由于后面的 90% 已经有序,我们只需考虑每个整理好的元素应该置于哪一个位置。对于这一步,我们可以采用二分查找来快速定位每个元素插入的位置,因为二分查找在有序数组中查找的时间复杂度为 O(log n)。

这种方法有效是因为它减少了数组前部分多余的比较和移动操作,而直接利用了后部分已经有序的属性。

现在,手动演示该方法应用到给出的数组的前半部分(未给出全部元素,只是示范过程):

初始未排序的部分:[3, 7, 4, 9, 5, 2, 6, 1, 14, 18]
初始已排序的部分:[16, 17, 20, 13, 12, 11, 19, 15, 12, 10, ...]
第一步:对未排序的部分进行插入排序
经过插入排序的未排序部分:[1, 2, 3, 4, 5, 6, 7, 9, 14, 18]
第二步:将排序好的这部分利用二分查找插入到后面的有序部分中
假设数组很长,我们只需要根据二分查找定位到具体位置并进行插入即可。
最终结果(只示范少量,实际需要做全部合并):
合并一些元素后:[1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...]

注意,实际操作时,由于数组可能非常大,不需要真的将有序部分进行物理移动,而是可以通过重新排列索引或者使用一个辅助数组来模拟插入操作。

四、思考

如果数组前部分已经有序而后部分无序,是否还可以采用其他算法进行优化?

总结

插入排序讲解结束了,很多情况下插入排序需要与其他算法结构才能够真正起到非常好的效果,所以我们还要继续学习不同的排序算法。

感谢大家抽出宝贵的时间来阅读博主的博客,新人博主,感谢大家关注点赞,祝大家未来的学习工作生活一帆风顺,加油!!!

目录
相关文章
|
3月前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
91 1
|
15天前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
76 29
|
15天前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
72 25
|
15天前
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
58 23
|
1月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
49 2
|
2月前
|
存储 运维 监控
探索局域网电脑监控软件:Python算法与数据结构的巧妙结合
在数字化时代,局域网电脑监控软件成为企业管理和IT运维的重要工具,确保数据安全和网络稳定。本文探讨其背后的关键技术——Python中的算法与数据结构,如字典用于高效存储设备信息,以及数据收集、异常检测和聚合算法提升监控效率。通过Python代码示例,展示了如何实现基本监控功能,帮助读者理解其工作原理并激发技术兴趣。
68 20
|
3月前
|
数据采集 存储 算法
Python 中的数据结构和算法优化策略
Python中的数据结构和算法如何进行优化?
|
3月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
99 1
|
3月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
328 9
|
3月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
53 1