采用 "维护有序数组 + 模拟局部调整" 的策略,避免了每次查询都重新排序,大幅提升了效率。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
整体思路
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
逐段代码详解
1. 数据结构定义
关键设计:
* a[] 数组始终按稳定排序规则有序
* pos[] 是反向映射:通过原始位置快速找到有序数组中的下标
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2. 比较函数(保证稳定性)
为什么这样写能保证稳定性?
情况 比较结果 排序后相对位置 a.value < b.value true a 在 b 前面 ✓ a.value > b.value false b 在 a 前面 ✓ a.value == b.value 且 a.id < b.id true a 在 b 前面 ✓ a.value == b.value 且 a.id > b.id false b 在 a 前面 ✓
✅ 值相同时,原始位置小的排前面 → 完全模拟题目中插入排序的稳定性!
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3. 初始化:建立有序状态
执行后状态:
* a[1..n]:按稳定规则排好序
* pos[id]:原始位置 id 的元素现在在 a[] 的哪个下标
示例:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4. 处理操作
🔹 修改操作 OP==1
为什么需要"向前+向后"两次冒泡?
因为修改后的值可能比原来更大或更小:
情况 示例 需要调整方向 值变小 [..., 5, 10, 15, ...] → 把10改成3 向前冒泡:3要移到5前面 值变大 [..., 5, 10, 15, ...] → 把10改成20 向后冒泡:20要移到15后面 值不变 无需移动 两次循环都会立即退出
模拟示例:
🔹 查询操作 OP==2
原理:pos[x] 始终记录原始位置 x 的元素在当前有序数组中的下标。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
总结
1. 稳定排序的cmp写法:值相等时按原始位置排序
2. 反向映射pos[]:用空间换时间,查询O(1)
3. 局部冒泡调整:修改后只需调整受影响的部分,不用全排
4. 及时更新映射:每次调整后必须同步更新pos[]
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
如果还是不理解,记住这三句话:
1. a[] 永远有序:像维护一个"实时排序"的数组
2. pos[] 是快捷方式:不用遍历找元素,直接查表
3. 修改=更新+局部调整:只动"受影响"的部分,类似插入排序的思想
> 类比理解:这就像维护一个"实时排行榜",有人分数变了,只需把他往前/往后挪到正确位置,不用重新排整个榜!
> 用额外空间(pos数组)和局部调整,换取查询效率的大幅提升