插入排序详解:如何让无序数组步步为营?

166 阅读5分钟

插入排序:如何将乱序的元素一步步整理成有序的图景

插入排序(Insertion Sort)是一种常见的排序算法,其最大特点是简单且直观。我们可以将插入排序的过程比作一个人整理扑克牌的过程:从第二张牌开始,拿到当前牌后就将它与已经整理好的牌堆中合适的位置进行比较,直到这张牌放到它应该所在的位置。依此类推,直到所有的牌都整理完毕,整个扑克牌序列也就完成了排序。

插入排序的基本思想

插入排序的工作原理就是将一个元素依次与前面的元素进行比较,并插入到一个正确的位置,使得部分有序的数组逐步扩展成一个完全有序的数组。这个过程的核心是通过“比较”与“插入”来逐步调整数组的顺序。

具体实现时,我们假设数组的第一个元素已经是“有序”的(因为只有一个元素不需要排序)。然后从第二个元素开始,逐个将每个元素插入到已排序部分的适当位置,直到整个数组有序。

插入排序的工作步骤

  1. 初始化有序子列表:数组的第一个元素默认被认为已经是有序的。
  2. 插入新元素:从数组的第二个元素开始,依次与已排序的部分进行比较,找到合适的位置。
  3. 元素插入:将当前元素插入到其应在位置,保证前半部分仍然是有序的。
  4. 重复操作:继续向后遍历,直到所有元素都插入到合适的位置。

插入排序的时间复杂度

插入排序的最坏时间复杂度是 O(n²),即当数据逆序排列时,插入排序需要进行大量的比较和移动,性能较差。然而,对于已经部分有序的数组,插入排序的表现会非常好。比如,如果一个数组仅需要少数几次交换就能变得有序,插入排序的效率会远高于许多其他复杂的排序算法。

插入排序的优缺点

优点:

  • 实现简单、易于理解。
  • 对于小规模数据或者部分已排序的数组,插入排序是一个非常高效的算法。
  • 是一种稳定的排序算法,即相等元素的相对顺序不会改变。

缺点:

  • 当数据规模较大时,时间复杂度为 O(n²),性能较差,尤其在逆序数组中。

插入排序的Java代码实现

我们可以用Java语言实现插入排序。下面是一个简单的实现代码:

public class InsertionSort {

    // 插入排序方法
    public static void insertionSort(int[] arr) {
        // 从数组的第二个元素开始,因为假设第一个元素已经是有序的
        for (int i = 1; i < arr.length; i++) {
            int current = arr[i];  // 当前要插入的元素
            int j = i - 1;  // 已排序部分的最后一个元素索引
            
            // 找到合适的位置插入当前元素
            while (j >= 0 && arr[j] > current) {
                arr[j + 1] = arr[j];  // 移动元素向右
                j--;  // 继续检查前面的元素
            }
            
            // 将当前元素插入到正确的位置
            arr[j + 1] = current;
        }
    }

    // 打印数组的方法
    public static void printArray(int[] arr) {
        for (int i : arr) {
            System.out.print(i + " ");
        }
        System.out.println();
    }

    // 主程序
    public static void main(String[] args) {
        int[] arr = {5, 2, 9, 1, 5, 6};
        
        System.out.println("原始数组:");
        printArray(arr);
        
        insertionSort(arr);  // 执行插入排序
        
        System.out.println("排序后的数组:");
        printArray(arr);  // 打印排序后的数组
    }
}

代码说明:

  1. insertionSort(int[] arr):这是插入排序的核心方法。它通过外层循环遍历数组的每一个元素,并通过内层 while 循环将当前元素插入到已排序部分的合适位置。
  2. printArray(int[] arr):用于打印数组,帮助我们在程序中查看排序前后的数组变化。
  3. main(String[] args):这是程序的入口方法,定义了一个示例数组,调用排序方法并打印排序前后的结果。

插入排序的执行过程示意

假设我们有一个数组 [5, 2, 9, 1, 5, 6],执行插入排序时的步骤如下:

  1. 初始数组[5, 2, 9, 1, 5, 6]
  2. 第一步(插入 2):我们将 25 比较,发现 2 小于 5,于是将 5 向右移动,插入 2 到数组前面。数组变为 [2, 5, 9, 1, 5, 6]
  3. 第二步(插入 9):9 大于 5,因此它保持在原位置,数组不变 [2, 5, 9, 1, 5, 6]
  4. 第三步(插入 1):1 小于 952,所以我们将它依次插入到最前面,数组变为 [1, 2, 5, 9, 5, 6]
  5. 第四步(插入 5):5 小于 9,但大于前面的元素 2,因此插入到 5 之后,数组变为 [1, 2, 5, 5, 9, 6]
  6. 第五步(插入 6):6 小于 9,但大于 5,所以插入到 9 前面,最终数组为 [1, 2, 5, 5, 6, 9]

结语

插入排序虽然在时间复杂度上表现不如其他高效的排序算法,如快速排序或归并排序,但它却以其简单易懂的实现和较高的局部排序效率,在处理部分有序数据时表现得异常优秀。特别是在数据量较小或近乎排序好的情况下,插入排序依然是一个非常不错的选择。