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

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

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



前言

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


一、什么是插入排序

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

  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, ...]

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

四、思考

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

总结

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

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

目录
相关文章
|
2月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
90 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
2月前
|
存储 缓存 算法
前端算法:优化与实战技巧的深度探索
【10月更文挑战第21天】前端算法:优化与实战技巧的深度探索
24 1
|
2月前
|
存储 算法 Java
Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性
Java Set因其“无重复”特性在集合框架中独树一帜。本文解析了Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性,并提供了最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的hashCode()与equals()方法。
40 4
|
2月前
|
搜索推荐 算法
数据结构与算法学习十四:常用排序算法总结和对比
关于常用排序算法的总结和对比,包括稳定性、内排序、外排序、时间复杂度和空间复杂度等术语的解释。
24 0
数据结构与算法学习十四:常用排序算法总结和对比
|
2月前
|
机器学习/深度学习 搜索推荐 算法
探索数据结构:初入算法之经典排序算法
探索数据结构:初入算法之经典排序算法
|
2月前
|
算法 Java 索引
数据结构与算法学习十五:常用查找算法介绍,线性排序、二分查找(折半查找)算法、差值查找算法、斐波那契(黄金分割法)查找算法
四种常用的查找算法:顺序查找、二分查找(折半查找)、插值查找和斐波那契查找,并提供了Java语言的实现代码和测试结果。
27 0
|
8天前
|
算法
基于GA遗传算法的PID控制器参数优化matlab建模与仿真
本项目基于遗传算法(GA)优化PID控制器参数,通过空间状态方程构建控制对象,自定义GA的选择、交叉、变异过程,以提高PID控制性能。与使用通用GA工具箱相比,此方法更灵活、针对性强。MATLAB2022A环境下测试,展示了GA优化前后PID控制效果的显著差异。核心代码实现了遗传算法的迭代优化过程,最终通过适应度函数评估并选择了最优PID参数,显著提升了系统响应速度和稳定性。
|
5天前
|
算法
基于WOA鲸鱼优化的购售电收益与风险评估算法matlab仿真
本研究提出了一种基于鲸鱼优化算法(WOA)的购售电收益与风险评估算法。通过将售电公司购售电收益风险计算公式作为WOA的目标函数,经过迭代优化计算出最优购电策略。实验结果表明,在迭代次数超过10次后,风险价值收益优化值达到1715.1万元的最大值。WOA还确定了中长期市场、现货市场及可再生能源等不同市场的最优购电量,验证了算法的有效性。核心程序使用MATLAB2022a实现,通过多次迭代优化,实现了售电公司收益最大化和风险最小化的目标。
|
3天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于深度学习网络的宝石类型识别算法matlab仿真
本项目利用GoogLeNet深度学习网络进行宝石类型识别,实验包括收集多类宝石图像数据集并按7:1:2比例划分。使用Matlab2022a实现算法,提供含中文注释的完整代码及操作视频。GoogLeNet通过其独特的Inception模块,结合数据增强、学习率调整和正则化等优化手段,有效提升了宝石识别的准确性和效率。
|
9天前
|
算法
基于大爆炸优化算法的PID控制器参数寻优matlab仿真
本研究基于大爆炸优化算法对PID控制器参数进行寻优,并通过Matlab仿真对比优化前后PID控制效果。使用MATLAB2022a实现核心程序,展示了算法迭代过程及最优PID参数的求解。大爆炸优化算法通过模拟宇宙大爆炸和大收缩过程,在搜索空间中迭代寻找全局最优解,特别适用于PID参数优化,提升控制系统性能。