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

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

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



前言

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


一、什么是插入排序

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

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

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

四、思考

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

总结

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

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

目录
相关文章
|
1月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
69 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
25天前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
57 2
|
22天前
|
存储 缓存 算法
前端算法:优化与实战技巧的深度探索
【10月更文挑战第21天】前端算法:优化与实战技巧的深度探索
19 1
|
30天前
|
存储 算法 Java
Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性
Java Set因其“无重复”特性在集合框架中独树一帜。本文解析了Set接口及其主要实现类(如HashSet、TreeSet)如何通过特定数据结构和算法确保元素唯一性,并提供了最佳实践建议,包括选择合适的Set实现类和正确实现自定义对象的hashCode()与equals()方法。
32 4
|
1月前
|
搜索推荐 算法
数据结构与算法学习十四:常用排序算法总结和对比
关于常用排序算法的总结和对比,包括稳定性、内排序、外排序、时间复杂度和空间复杂度等术语的解释。
19 0
数据结构与算法学习十四:常用排序算法总结和对比
|
1月前
|
机器学习/深度学习 搜索推荐 算法
探索数据结构:初入算法之经典排序算法
探索数据结构:初入算法之经典排序算法
|
1月前
|
算法 Java 索引
数据结构与算法学习十五:常用查找算法介绍,线性排序、二分查找(折半查找)算法、差值查找算法、斐波那契(黄金分割法)查找算法
四种常用的查找算法:顺序查找、二分查找(折半查找)、插值查找和斐波那契查找,并提供了Java语言的实现代码和测试结果。
19 0
|
14天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
5天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
15 1
|
8天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。

热门文章

最新文章