插入排序(Insertion Sort)
字数 2007 2025-12-10 10:24:11

插入排序(Insertion Sort)

题目描述
给定一个长度为 n 的整数数组 nums,请实现插入排序算法对该数组进行原地升序排序。插入排序的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。请解释算法步骤,分析其时间复杂度和空间复杂度,并讨论其适用场景。

解题过程循序渐进讲解

1. 核心思想
插入排序模拟了我们整理扑克牌的过程:左手持已排序的牌,右手从桌上摸一张新牌,然后将新牌插入左手已排序牌中的正确位置。对于数组,我们可以将第一个元素视为已排序序列(初始长度为1),然后依次将后续元素插入到已排序序列的合适位置,直到整个数组有序。

2. 算法步骤详述
假设数组 nums 的下标从 0 到 n-1。

  • 步骤1:初始时,我们认为 nums[0] 是已排序序列(长度为1)。i 从 1 开始遍历到 n-1,i 指向当前待插入的元素。
  • 步骤2:将当前待插入元素 nums[i] 保存在一个临时变量 key 中(避免后续移动元素时被覆盖)。
  • 步骤3:设置指针 j = i - 1,从已排序序列的末尾(即 i-1 位置)开始向前扫描。
  • 步骤4:在扫描过程中,比较 key 与 nums[j]。如果 nums[j] > key,说明 nums[j] 应该位于 key 之后,因此将 nums[j] 向后移动一位(赋值给 nums[j+1])。然后 j 减1,继续向前比较。
  • 步骤5:当遇到以下两种情况之一时,停止向前扫描并插入 key:
    a) 找到一个位置 j,使得 nums[j] <= key(此时 key 应插入到 j+1 位置)。
    b) j 减到 -1(即已扫描完整个已排序序列,key 是当前最小元素,应插入到开头)。
  • 步骤6:将 key 插入到位置 j+1(即 nums[j+1] = key)。
  • 步骤7:重复步骤2~6,直到 i 遍历完整个数组。

3. 示例说明
以数组 nums = [5, 2, 4, 6, 1, 3] 为例,展示其逐步排序过程。

初始状态:[5, 2, 4, 6, 1, 3]

  • i=1: key=2。j从0开始,5>2,所以5右移得[5,5,4,6,1,3],j=-1停止,插入key到j+1=0,得[2,5,4,6,1,3]。
  • i=2: key=4。j=1,5>4,5右移得[2,5,5,6,1,3];j=0,2<=4,停止,插入key到j+1=1,得[2,4,5,6,1,3]。
  • i=3: key=6。j=2,5<=6,停止,插入key到j+1=3,得[2,4,5,6,1,3](位置不变)。
  • i=4: key=1。j=3,6>1,右移得[2,4,5,6,6,3];j=2,5>1,右移得[2,4,5,5,6,3];j=1,4>1,右移得[2,4,4,5,6,3];j=0,2>1,右移得[2,2,4,5,6,3];j=-1,停止,插入key到0,得[1,2,4,5,6,3]。
  • i=5: key=3。j=4,6>3,右移得[1,2,4,5,6,6];j=3,5>3,右移得[1,2,4,5,5,6];j=2,4>3,右移得[1,2,4,4,5,6];j=1,2<=3,停止,插入key到j+1=2,得[1,2,3,4,5,6]。
    排序完成。

4. 代码实现(Python)

def insertion_sort(nums):
    n = len(nums)
    for i in range(1, n):
        key = nums[i]          # 待插入元素
        j = i - 1
        # 从后向前扫描已排序序列,移动比 key 大的元素
        while j >= 0 and nums[j] > key:
            nums[j + 1] = nums[j]
            j -= 1
        nums[j + 1] = key     # 插入 key
    return nums

5. 复杂度分析

  • 时间复杂度
    • 最好情况:数组已完全有序,内层 while 循环每次只比较一次就退出,总比较次数为 n-1,移动次数为0,时间复杂度为 O(n)。
    • 最坏情况:数组完全逆序,每个元素都需要移动到最前面。第 i 个元素需要比较 i 次、移动 i 次,总比较和移动次数约为 n(n-1)/2,时间复杂度为 O(n²)。
    • 平均情况:时间复杂度为 O(n²)。
  • 空间复杂度:只使用了常数个临时变量(如 key, i, j),是原地排序,空间复杂度为 O(1)。
  • 稳定性:当遇到相等元素时(nums[j] > key 的条件是严格大于,等于时不移动),不会改变相等元素的相对顺序,因此是稳定排序。

6. 特点与适用场景

  • 优点
    • 实现简单,代码紧凑。
    • 对小规模数据或基本有序数据效率很高(接近 O(n))。
    • 原地、稳定排序。
    • 在线算法:可以逐个接收数据并实时排序。
  • 缺点:对大规模乱序数据效率低,O(n²) 的时间复杂度在大数据下不实用。
  • 适用场景
    • 小规模数组排序(如 n ≤ 20)。
    • 部分有序数组的排序(如日志按时间插入)。
    • 作为高级排序算法的子过程(例如,在快速排序或归并排序中,当递归到小规模子问题时,切换到插入排序可提升性能)。
  • 实际应用举例:在标准库的混合排序算法(如 Timsort)中,当分段长度较小时常用插入排序处理。
插入排序(Insertion Sort) 题目描述 给定一个长度为 n 的整数数组 nums,请实现插入排序算法对该数组进行原地升序排序。插入排序的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。请解释算法步骤,分析其时间复杂度和空间复杂度,并讨论其适用场景。 解题过程循序渐进讲解 1. 核心思想 插入排序模拟了我们整理扑克牌的过程:左手持已排序的牌,右手从桌上摸一张新牌,然后将新牌插入左手已排序牌中的正确位置。对于数组,我们可以将第一个元素视为已排序序列(初始长度为1),然后依次将后续元素插入到已排序序列的合适位置,直到整个数组有序。 2. 算法步骤详述 假设数组 nums 的下标从 0 到 n-1。 步骤1 :初始时,我们认为 nums[ 0 ] 是已排序序列(长度为1)。i 从 1 开始遍历到 n-1,i 指向当前待插入的元素。 步骤2 :将当前待插入元素 nums[ i ] 保存在一个临时变量 key 中(避免后续移动元素时被覆盖)。 步骤3 :设置指针 j = i - 1,从已排序序列的末尾(即 i-1 位置)开始向前扫描。 步骤4 :在扫描过程中,比较 key 与 nums[ j]。如果 nums[ j] > key,说明 nums[ j] 应该位于 key 之后,因此将 nums[ j] 向后移动一位(赋值给 nums[ j+1 ])。然后 j 减1,继续向前比较。 步骤5 :当遇到以下两种情况之一时,停止向前扫描并插入 key: a) 找到一个位置 j,使得 nums[ j] <= key(此时 key 应插入到 j+1 位置)。 b) j 减到 -1(即已扫描完整个已排序序列,key 是当前最小元素,应插入到开头)。 步骤6 :将 key 插入到位置 j+1(即 nums[ j+1 ] = key)。 步骤7 :重复步骤2~6,直到 i 遍历完整个数组。 3. 示例说明 以数组 nums = [ 5, 2, 4, 6, 1, 3 ] 为例,展示其逐步排序过程。 初始状态:[ 5, 2, 4, 6, 1, 3 ] i=1: key=2。j从0开始,5>2,所以5右移得[ 5,5,4,6,1,3],j=-1停止,插入key到j+1=0,得[ 2,5,4,6,1,3 ]。 i=2: key=4。j=1,5>4,5右移得[ 2,5,5,6,1,3];j=0,2<=4,停止,插入key到j+1=1,得[ 2,4,5,6,1,3 ]。 i=3: key=6。j=2,5<=6,停止,插入key到j+1=3,得[ 2,4,5,6,1,3 ](位置不变)。 i=4: key=1。j=3,6>1,右移得[ 2,4,5,6,6,3];j=2,5>1,右移得[ 2,4,5,5,6,3];j=1,4>1,右移得[ 2,4,4,5,6,3];j=0,2>1,右移得[ 2,2,4,5,6,3];j=-1,停止,插入key到0,得[ 1,2,4,5,6,3 ]。 i=5: key=3。j=4,6>3,右移得[ 1,2,4,5,6,6];j=3,5>3,右移得[ 1,2,4,5,5,6];j=2,4>3,右移得[ 1,2,4,4,5,6];j=1,2<=3,停止,插入key到j+1=2,得[ 1,2,3,4,5,6 ]。 排序完成。 4. 代码实现(Python) 5. 复杂度分析 时间复杂度 : 最好情况:数组已完全有序,内层 while 循环每次只比较一次就退出,总比较次数为 n-1,移动次数为0,时间复杂度为 O(n)。 最坏情况:数组完全逆序,每个元素都需要移动到最前面。第 i 个元素需要比较 i 次、移动 i 次,总比较和移动次数约为 n(n-1)/2,时间复杂度为 O(n²)。 平均情况:时间复杂度为 O(n²)。 空间复杂度 :只使用了常数个临时变量(如 key, i, j),是原地排序,空间复杂度为 O(1)。 稳定性 :当遇到相等元素时(nums[ j ] > key 的条件是严格大于,等于时不移动),不会改变相等元素的相对顺序,因此是稳定排序。 6. 特点与适用场景 优点 : 实现简单,代码紧凑。 对小规模数据或基本有序数据效率很高(接近 O(n))。 原地、稳定排序。 在线算法:可以逐个接收数据并实时排序。 缺点 :对大规模乱序数据效率低,O(n²) 的时间复杂度在大数据下不实用。 适用场景 : 小规模数组排序(如 n ≤ 20)。 部分有序数组的排序(如日志按时间插入)。 作为高级排序算法的子过程(例如,在快速排序或归并排序中,当递归到小规模子问题时,切换到插入排序可提升性能)。 实际应用举例 :在标准库的混合排序算法(如 Timsort)中,当分段长度较小时常用插入排序处理。