简单排序算法
冒泡排序
冒泡排序是一种交换类排序算法。它的核心思想是通过重复走访要排序的数列,依次比较相邻的两个元素。如果它们的顺序错误(比如前面的比后面的大),就交换过来。就像水底的气泡一样,越大的元素会经由交换慢慢 “浮” 到数列的顶端。冒泡排序虽然在处理大规模乱序数据时效率较低,但它具有实现简单、不占额外内存且保持顺序稳定的优点。它是理解算法思维、循环控制逻辑以及 “空间换时间” 优化理念的最佳入门案例。
冒泡排序演示
当前状态:
准备就绪
1 | /** |
可以做的两个优化点:
- 标志位优化(NeedNext):记录当前轮次是否发生了交换。若未交换,说明数组已有序,直接提前终止。
- 记录最后交换位置:进一步精确下一轮扫描的范围(请读者自行实现试试 ^_^)。
选择排序
排序的原理:
- 每一次遍历的过程中,都假定第一个索引处的元素是最小值,和其他索引处的值依次比较,如果当前索引处的值大于其他某个索引处的值,则假定其他某个索引处的值为最小值,最后可以找到最小值所在的索引。
- 交换第一个索引处和最小值所在的索引处的值。
一般情况下,选择排序比冒泡排序的交换次数更少。在冒泡排序中,为了把最大的数挪到末尾,可能需要一路交换十几次。而在选择排序中,无论数组多乱,每一轮内层循环只为了寻找那个“天选之子”的索引,最后只动手交换一次。但排序算法是不稳定的,本质上是因为长距离的交换可能会破坏相同元素的相对顺序。举个栗子:假设数组是 {5, 5, 2}(为了区分,我们叫它们 5a, 5b,2)。
- 第一轮扫描,发现最小值是 2。
- 2 与第一个位置的 5a 交换。
- 数组变成了 {2, 5b, 5a}。
选择排序演示
当前状态:
准备就绪
1 | /** |
插入排序
插入排序是 O(n^2) 家族中最有 “人情味” 的一个。它非常像我们玩扑克牌时整理手牌的过程:右手抓起一张新牌,然后在左手已排好序的牌堆中,从右往左摸索,直到找到它的位置并插进去。排序原理如下:
- 把所有的元素分成两组,已经排好序的和未排序的;
- 找到未排序的第一个元素,向已经排序的组中进行插入;
- 倒序遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入的元素,那么就把这个元素放到这个位置,其他的元素向后移一位。
虽然它的平均时间复杂度也是 O(n^2),但在实际应用中,它往往比冒泡和选择排序表现得更好。
- 适应性:如果数组本身已经接近有序,插入排序的效率极高。在最好情况下,它只需要 O(n) 时间。
- 稳定性:”if (array[j] < array[j-1])” 保证只有在严格小于时才交换,相等元素不会变位置,因此它是稳定的。
- 在线算法:它可以在接收新数据的同时进行排序,不需要等所有数据到齐。
插入排序演示
当前状态:
准备就绪
1 | /** |
我们注意到,在 Java 的 Arrays.sort() 或者其他语言的底层库中,针对小规模数据(比如 n \< 47),往往会优先选择插入排序而不是快速排序。原因就在于插入排序代码极其简单,没有递归开销,且对于接近有序的数据有几乎 O(n) 的表现,这在处理小规模数据时比快速排序更稳。