0088.合并两个有序数组
难度:🟢 简单
标签:数组
、双指针
、排序
链接:88. 合并两个有序数组
题目描述
给你两个按 非递减顺序 排列的整数数组 nums1
和 nums2
,另有两个整数 m
和 n
,分别表示 nums1
和 nums2
中的元素数目。
请你 合并 nums2
到 nums1
中,使合并后的数组同样按 非递减顺序 排列。
注意: 最终,合并后的数组不应由函数返回,而是存储在数组 nums1
中。为了应对这种情况,nums1
的初始长度为 m + n
,其中前 m
个元素表示应合并的元素,后 n
个元素为 0
,应忽略。nums2
的长度为 n
。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] 。
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。
示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。注意因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
解题思路
核心思想
本题的核心思想是 从后向前 进行合并。因为 nums1
数组的尾部有足够的空闲空间,从后向前填充可以避免在合并过程中覆盖掉 nums1
中尚未被比较的元素。如果从前向后合并,则需要移动 nums1
的元素来为 nums2
的元素腾出空间,这将导致 O(m*n) 的时间复杂度。
思路选择
采用 三指针法 是解决此问题的最优策略。
一个指针
p1
指向nums1
有效元素部分的末尾(即索引m-1
)。一个指针
p2
指向nums2
的末尾(即索引n-1
)。一个指针
p
指向nums1
数组的最末尾(即索引m+n-1
),这是合并后最大元素应该存放的位置。
通过比较 p1
和 p2
指向的元素大小,将较大的元素放入 p
指向的位置,然后移动相应的指针,如此往复,直到合并完成。
关键步骤
初始化:初始化三个指针
p1 = m - 1
,p2 = n - 1
,p = m + n - 1
。从后向前遍历:当
p2
指针仍然有效时(即p2 >= 0
),持续循环。这个条件是关键,因为只要nums2
的元素还没被完全合并,我们就需要继续操作。比较与放置:
在循环中,比较
nums1[p1]
和nums2[p2]
的值。情况一:如果
p1
仍有效(p1 >= 0
)且nums1[p1]
大于nums2[p2]
,则将nums1[p1]
放到nums1[p]
的位置,并将p1
向前移动。情况二:否则(即
p1
已越界或nums2[p2]
更大或相等),将nums2[p2]
放到nums1[p]
的位置,并将p2
向前移动。
移动插入指针:每放置一个元素后,都需要将插入指针
p
向前移动。结束:当
p2 < 0
时,循环结束。此时所有nums2
的元素都已合并到nums1
中。如果nums1
中还有剩余元素(即p1 >= 0
),它们已经在正确的位置上了,无需任何操作。
代码实现
/**
* @param {number[]} nums1
* @param {number} m
* @param {number[]} nums2
* @param {number} n
* @return {void} Do not return anything, modify nums1 in-place instead.
*/
var merge = function (nums1, m, nums2, n) {
let p1 = m - 1; // 指向 nums1 有效部分的末尾
let p2 = n - 1; // 指向 nums2 的末尾
let p = m + n - 1; // 指向 nums1 的最末尾
// 只要 nums2 中还有元素待合并
while (p2 >= 0) {
// 如果 p1 还有效,且 p1 指向的元素大于 p2 指向的元素
if (p1 >= 0 && nums1[p1] > nums2[p2]) {
nums1[p] = nums1[p1];
p1--;
} else {
// 否则(p1越界或p2指向的元素更大),将 p2 指向的元素放入
nums1[p] = nums2[p2];
p2--;
}
// 移动插入指针
p--;
}
};
复杂度分析
时间复杂度:O(m + n) 指针
p1
和p2
总共只会完整地遍历m
和n
个元素一次。空间复杂度:O(1) 我们只使用了常数个额外变量作为指针,合并操作是在
nums1
数组内部完成的(in-place),没有使用与输入规模成正比的额外空间。
相关题目
总结
本题是双指针思想的经典应用。通过 逆向双指针 的方法,巧妙地解决了在数组原地合并时可能发生的数据覆盖问题,从而实现了高效且空间占用小的解决方案。理解这种从后向前的操作思路是掌握本题的关键。