← 双指针 · Two Pointer / 合并两个有序数组:从后往前填,避免覆盖 待审核 2 / 7
Merge Sorted Array · 逆向

合并两个有序数组:从后往前填,避免覆盖

LeetCode 88:nums1m 个是有序数据、后 n 个是预留空位;nums2n 个有序数。要把两者合并、原地排好放进 nums1。两个数组都有序,典型双指针——但如果像合并链表那样从前往后填,新写进 nums1 前部的数会覆盖掉尚未比较的原数据,要避开就得不断向后挪动元素,代价很高。

解决办法是反转处理方向:从后往前填就不存在覆盖问题——nums1 末尾那 n 个空位本就空闲,可任意写入。用三个指针:p1 指 nums1 有效部分的尾、p2 指 nums2 的尾、p 指 nums1 最末的写入位。每次把 p1、p2 处更大的那个写入 p,然后对应指针一起退一格。

默认 nums1=[1,2,3,,,]nums1 = [1,2,3,\cdot ,\cdot ,\cdot ](m=3)+ nums2 = [2,5,6]\cdot 是预留空位。重点关注:写指针 p 永远在读指针 p1 的右边(因为剩下要合并的元素个数 = p1+p2+2 ≤ p+1),所以从后往前写绝不会覆盖尚未读取的数。

为什么从后往前就解决了覆盖?写指针 p 要填的是「最大的、最该靠后的数」,而它们来自 p1 / p2 的尾部——那些位置已经被读过、即将向前回退。任意时刻待合并元素还剩 (p1+1)+(p2+1) 个,而 p 右边的空位有 p+1 个,恒有 pp1p \ge p1,写头永远追不上读头。遇到原地操作会互相覆盖时,不妨先考虑能否反向处理。

收尾只补 nums2 的剩余:如果先耗尽 nums2,nums1 自己剩下的那段本来就在正确位置上(它们比已填入的都小、且原本有序),一个都不用动。同样的「双列表归并对齐」手法,在稀疏向量点积里用于求两个有序列表的交集。