CSP-J 2021普及组题解T2:插入排序

本文介绍了CSP-J 2021普及组的题目,涉及插入排序算法的实现及操作模拟。文章详细解析了如何处理在插入排序过程中对数组的修改(操作1)和查询排序后的位置(操作2),并提供了朴素版和优化版的解决方案,优化版利用插入排序的特性减少调整次数。

题目描述

插入排序是一种非常常见且简单的排序算法。小 Z 是一名大一的新生,今天 H 老师刚刚在上课的时候讲了插入排序算法。假设比较两个元素的时间为 O(1)\mathcal O(1)O(1),则插入排序可以以 O(n2)\mathcal O(n^2)O

<think>题目要求:根据题目描述,我们需要实现一个插入排序的优化版本,其中在插入过程中使用二分查找来找到插入位置,从而减少比较次数。 题目背景:CSP-J 2021 T2 插入排序 题目描述:插入排序一种非常简单排序算法,其基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增加1的有序表。 在插入过程中,我们可以使用二分查找来确定插入位置,以减少比较操作的次数。 我们需要实现一个函数,该函数接受一个整数数组和数组的长度,然后使用插入排序(在查找插入位置时使用二分查找)对数组进行排序。 步骤: 1. 从第一个元素开始,该元素可以认为已经被排序。 2. 取出下一个元素,在已经排序的元素序列中二分查找到第一个比它大的数的位置。 3. 将新元素插入到该位置后,并移动数组元素。 注意:由于数组插入需要移动元素,所以整体时间复杂度仍然是O(n^2),但比较次数减少到O(n log n)。 代码实现思路: 外层循环:遍历从1到n-1的每个元素(i从1开始),将当前元素arr[i]作为待插入元素。 内层使用二分查找在已排序部分(0到i-1)中查找插入位置。 二分查找:在已排序的区间[0, i-1]中,查找第一个大于等于arr[i]的元素的位置,这个位置就是插入位置。 找到位置后,将插入位置到i-1的元素都后移一位,然后将待插入元素放入插入位置。 注意:二分查找时,如果存在多个相同的元素,我们找到的是第一个大于等于当前元素的位置,这样排序是稳定的吗? 实际上,由于我们查找的是第一个大于等于当前元素的位置,然后插入到该位置,所以相同元素的相对顺序不会改变(因为当前元素被插入到已存在元素的后面),因此是稳定的。 但是,标准的插入排序是稳定的,我们这里使用二分查找并不会破坏稳定性,因为移动元素时,相同元素不会被越过。 具体步骤: 1. 令key = arr[i] 2. 使用二分查找在arr[0..i-1]中找到插入位置pos,即第一个大于等于key的元素的位置。 3. 将arr[pos..i-1]的元素都向后移动一位(从后往前移动,避免覆盖)。 4. 将key赋值给arr[pos]。 注意:移动元素时,应该从i-1开始,到pos为止,逐个向后移动。 代码实现: 二分查找函数(也可以直接写在排序循环中): low = 0, high = i-1 while low <= high: mid = (low + high) // 2 if arr[mid] > key: # 因为我们要找第一个大于等于key的位置,所以当mid位置大于key时,说明插入位置在mid左边(包括mid) high = mid - 1 else: # 当arr[mid] <= key,说明插入位置在mid右边(不包括mid) low = mid + 1 最后,low的位置就是第一个大于等于key的位置,即插入位置。 为什么? 因为当循环结束时,low指向第一个大于key的元素,而high指向最后一个小于等于key的元素。 所以插入位置就是low。 注意:二分查找的区间是[0, i-1],所以返回的low一定在[0, i]之间(当所有元素都小于等于key时,low=i,即插入在最后)。 然后进行移动:从i-1开始,到low(包括low),每个元素向后移动一位,最后将key放在arr[low]。 代码实现如下:</think>### 插入排序(二分查找优化版)实现 以下是使用 Python 实现的插入排序算法,其中在查找插入位置时使用了二分查找优化,显著减少了比较次数(从 O(n&sup2;) 降到 O(n log n)): ```python def binary_search(arr, key, end): """ 在已排序数组 arr[0..end-1] 中查找第一个大于 key 的元素位置 """ left, right = 0, end while left < right: mid = (left + right) // 2 if arr[mid] <= key: left = mid + 1 else: right = mid return left def insertion_sort(arr): n = len(arr) for i in range(1, n): key = arr[i] # 在已排序部分 arr[0..i-1] 中二分查找插入位置 pos = binary_search(arr, key, i) # 移动元素:从 pos 到 i-1 的元素全部后移一位 j = i while j > pos: arr[j] = arr[j-1] j -= 1 # 插入元素 arr[pos] = key return arr # 测试示例 if __name__ == "__main__": test_arr = [24, 13, 15, 38, 5, 2] print("排序前:", test_arr) sorted_arr = insertion_sort(test_arr) print("排序后:", sorted_arr) ``` ### 代码解释 1. **`binary_search` 函数**: - 在已排序子数组 `arr[0..end-1]` 中查找第一个大于 `key` 的元素位置 - 使用左闭右开区间 `[left, right)` 进行二分查找 - 时间复杂度:O(log n) 2. **`insertion_sort` 函数**: - **外层循环**:遍历每个待插入元素(从索引 1 开始) - **二分查找**:确定当前元素 `key` 在已排序子数组中的插入位置 - **元素移动**:将插入位置后的元素向后移动一位(时间复杂度 O(n)) - **元素插入**:将 `key` 放入正确位置 - 整体时间复杂度:O(n&sup2;)(移动操作仍是瓶颈),比较次数优化为 O(n log n) 3. **算法特性**: - **稳定性**:当 `arr[mid] == key` 时继续向右查找,保持相等元素的原始顺序 - **原地排序**:仅需 O(1) 额外空间 - **适用场景**:小规模数据或基本有序的数据集 ### 执行示例 输入:`[24, 13, 15, 38, 5, 2]` 输出:`[2, 5, 13, 15, 24, 38]` 排序过程演示: ``` 步骤1: [13, 24] 15, 38, 5, 2 ← 插入13 步骤2: [13, 15, 24] 38, 5, 2 ← 插入15 步骤3: [13, 15, 24, 38] 5, 2 ← 插入38 步骤4: [5, 13, 15, 24, 38] 2 ← 插入5 步骤5: [2, 5, 13, 15, 24, 38] ← 插入2 ```
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少儿编程乔老师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值