深入理解【插入排序】:原理、实现与优化

深入理解【插入排序】:原理、实现与优化

|-----------------------------|
| 🌺The Begin🌺点点关注,收藏不迷路🌺 |

一、算法概述

插入排序(Insertion Sort)是一种简单直观的基于比较的排序算法,其核心思想类似于我们整理扑克牌的方式。算法通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

基本特性

  • 时间复杂度
    • 最优情况(已排序数组):O(n)
    • 最坏情况(逆序数组):O(n²)
    • 平均情况:O(n²)
  • 空间复杂度:O(1)(原地排序)
  • 稳定性:稳定排序算法

二、算法原理详解

核心思想

  1. 将数组分为已排序未排序两部分
  2. 初始时已排序部分只包含第一个元素
  3. 每次从未排序部分取出第一个元素,与已排序部分的元素从后向前比较
  4. 找到合适位置后插入,使已排序部分保持有序
  5. 重复直到所有元素都被处理

算法流程图示

初始数组:[7, 6, 9, 3, 1]
markdown 复制代码
┌───┬───┬───┬───┬───┐
│ 7 │ 6 │ 9 │ 3 │ 1 │
└───┴───┴───┴───┴───┘
▲
已排序部分
第一轮:插入6(i=1)
markdown 复制代码
[比较6和7]
┌───┬───┬───┬───┬───┐
│ 7 │ 6 │ 9 │ 3 │ 1 │
└───┴───┴───┴───┴───┘
   ▲
   6 < 7 → 交换

[结果]
┌───┬───┬───┬───┬───┐
│ 6 │ 7 │ 9 │ 3 │ 1 │
└───┴───┴───┴───┴───┘
      ▲
      已排序部分
第二轮:插入9(i=2)
markdown 复制代码
[比较9和7]
┌───┬───┬───┬───┬───┐
│ 6 │ 7 │ 9 │ 3 │ 1 │
└───┴───┴───┴───┴───┘
         ▲
         9 > 7 → 不交换

[结果]
┌───┬───┬───┬───┬───┐
│ 6 │ 7 │ 9 │ 3 │ 1 │
└───┴───┴───┴───┴───┘
            ▲
            已排序部分
第三轮:插入3(i=3)
markdown 复制代码
[步骤1:比较3和9]
┌───┬───┬───┬───┬───┐
│ 6 │ 7 │ 9 │ 3 │ 1 │
└───┴───┴───┴───┴───┘
            ▲
            3 < 9 → 交换

[步骤2:比较3和7]
┌───┬───┬───┬───┬───┐
│ 6 │ 7 │ 3 │ 9 │ 1 │
└───┴───┴───┴───┴───┘
         ▲
         3 < 7 → 交换

[步骤3:比较3和6]
┌───┬───┬───┬───┬───┐
│ 6 │ 3 │ 7 │ 9 │ 1 │
└───┴───┴───┴───┴───┘
   ▲
   3 < 6 → 交换

[最终结果]
┌───┬───┬───┬───┬───┐
│ 3 │ 6 │ 7 │ 9 │ 1 │
└───┴───┴───┴───┴───┘
               ▲
               已排序部分
第四轮:插入1(i=4)
markdown 复制代码
[完整过程]
┌───┬───┬───┬───┬───┐   ┌───┬───┬───┬───┬───┐
│ 3 │ 6 │ 7 │ 9 │ 1 │ → │ 1 │ 3 │ 6 │ 7 │ 9 │
└───┴───┴───┴───┴───┘   └───┴───┴───┴───┴───┘
               ▲                    ▲
               经过4次比较和交换     排序完成

三、Java实现与优化

基础实现

java 复制代码
public class InsertionSort {
    public static void sort(Comparable[] arr) {
        int n = arr.length;
        for (int i = 1; i < n; i++) {
            for (int j = i; j > 0 && less(arr[j], arr[j-1]); j--) {
                swap(arr, j, j-1);
            }
        }
    }
    
    private static boolean less(Comparable v, Comparable w) {
        return v.compareTo(w) < 0;
    }
    
    private static void swap(Object[] arr, int i, int j) {
        Object temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

优化版本(减少元素移动)

java 复制代码
public static void optimizedSort(Comparable[] arr) {
    int n = arr.length;
    for (int i = 1; i < n; i++) {
        Comparable key = arr[i];
        int j = i-1;
        
        // 将比key大的元素后移
        while (j >= 0 && less(key, arr[j])) {
            arr[j+1] = arr[j];
            j--;
        }
        arr[j+1] = key;
    }
}

性能对比

实现方式 比较次数 交换/移动次数 适合场景
基础实现 O(n²) O(n²) 教学示例
优化实现 O(n²) O(n) 实际应用

四、算法分析

时间复杂度分析

  1. 最佳情况:数组已经有序

    • 每次只需要比较一次,共n-1次比较
    • 时间复杂度:O(n)
  2. 最坏情况:数组完全逆序

    • 需要进行1+2+...+(n-1)=n(n-1)/2次比较和交换
    • 时间复杂度:O(n²)
  3. 平均情况:O(n²)

空间复杂度

  • 只需要常数级别的额外空间(用于临时存储)
  • 空间复杂度:O(1)

五、实际应用场景

  1. 小规模数据排序:当n较小时(通常n<50),插入排序性能优于更复杂的算法
  2. 近乎有序的数据:对于大部分已排序的数据效率接近O(n)
  3. 作为高级算法的组成部分
    • 快速排序在小规模子数组时切换到插入排序
    • Timsort中的插入排序优化

六、与其他排序算法的对比

算法 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性
插入排序 O(n²) O(n²) O(1) 稳定
冒泡排序 O(n²) O(n²) O(1) 稳定
选择排序 O(n²) O(n²) O(1) 不稳定
快速排序 O(nlogn) O(n²) O(logn) 不稳定

七、扩展思考

  1. 二分插入排序:在查找插入位置时使用二分查找,可将比较次数降至O(nlogn),但移动次数仍为O(n²)
  2. 希尔排序:插入排序的改进版,通过分组插入排序来提高效率
  3. 链表实现:插入排序特别适合链表结构,因为不需要大量移动元素

总结

java 复制代码
package com.kwan.springbootkwan.controller;

/**
 * 插入排序
 */
public class InsertionSort {

    /**
     * 基础版本插入排序
     * @param arr 待排序数组
     */
    public static void basicSort(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return;
        }

        int n = arr.length;
        // 从第二个元素开始(i=1),因为第一个元素默认是有序的
        for (int i = 1; i < n; i++) {
            // 将当前元素与已排序部分的元素从后往前比较
            for (int j = i; j > 0; j--) {
                // 如果当前元素比前一个元素小,则交换
                if (arr[j] < arr[j - 1]) {
                    swap(arr, j, j - 1);
                } else {
                    // 否则说明已经找到合适位置,可以提前终止内层循环
                    break;
                }
            }
        }
    }

    // 交换数组中两个元素的位置
    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

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

    // 测试代码
    public static void main(String[] args) {
        // 测试基础版本
        int[] arr = {7, 6, 9, 3, 1};
        System.out.println("原始数组:");
        printArray(arr);

        System.out.println("\n基础版本排序后:");
        basicSort(arr);
        printArray(arr);
    }
}

插入排序虽然简单,但它体现了分治思想(将问题分为已排序和未排序部分),是理解更复杂算法的基础。其最佳情况下的线性时间复杂度使其在特定场景下非常高效,而简单的实现方式也使其成为算法教学的经典案例。

|---------------------------|
| 🌺The End🌺点点关注,收藏不迷路🌺 |

相关推荐
Piar1231sdafa2 小时前
深度学习目标检测算法之YOLOv26加拿大鹅检测
深度学习·算法·目标检测
闻缺陷则喜何志丹2 小时前
【C++DFS 马拉车】3327. 判断 DFS 字符串是否是回文串|2454
c++·算法·深度优先·字符串·力扣·回文·马拉车
我是小疯子662 小时前
HybridA*算法:高效路径规划核心解析
人工智能·算法·机器学习
晨非辰2 小时前
【数据结构入坑指南(三.1)】--《面试必看:单链表与顺序表之争,读懂“不连续”之美背后的算法思想》
数据结构·c++·人工智能·深度学习·算法·机器学习·面试
kamisama_zhu2 小时前
LeetCode 热题100快速通关指南(附模板) (优化完整版,真人心得版,持续更新)
算法·leetcode·职场和发展
h×32 小时前
LIO-SAM算法仿真实现教程(基于ubuntu22.04和ROS2-humble系统+GTSAM4.1.1)
算法
草莓熊Lotso2 小时前
《算法闯关指南:优选算法--滑动窗口》--15.串联所有单词的子串,16.最小覆盖子串
开发语言·c++·人工智能·算法
小李独爱秋3 小时前
模拟面试:什么是微服务架构,它的优缺点是什么?
算法·微服务·面试·职场和发展·框架·架构师
流云鹤3 小时前
2026牛客寒假算法基础集训营3(A B G J H F C)
算法