力扣88.合并两个有序数组

合并两个有序数组题解(详解三种方法:直接合并、双指针正序、双指针逆序)
一、题目回顾
给定两个按 非递减顺序 排列的整数数组 nums1 和 nums2,其中:
nums1的前m个元素为有效数据,后n个元素为 0(预留空间);nums2的长度为n;- 要求将
nums2合并到nums1中,使得nums1成为一个新的有序数组; - 函数不需要返回值,直接在原地修改
nums1。
示例
输入:nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
二、题目分析
题目要求:
- 数组非递减(即升序,可有重复);
- 在原数组
nums1中合并结果; - 空间有限,尽可能在 O(1) 额外空间内完成;
- 时间复杂度要求为 O(m + n)。
核心挑战在于:
如何避免提前覆盖掉
nums1的有效元素。
如果我们从前往后直接合并,当 nums1 较小时可能会被覆盖。因此,需要考虑从后往前合并的策略。
三、方法一:直接合并 + 排序(简单但效率不高)
思路
- 先将
nums2中的元素直接放入nums1的尾部空位; - 调用排序函数(如
Arrays.sort)对整个nums1排序。
代码实现
import java.util.Arrays;
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
for (int i = 0; i < n; i++) {
nums1[m + i] = nums2[i];
}
Arrays.sort(nums1);
}
}

复杂度分析
- 时间复杂度:O((m + n) log(m + n))
- 空间复杂度:O(1)
优缺点
- 优点:实现简单,几行代码即可;
- 缺点:没有利用数组原有的有序性,不符合题目的“进阶要求”。
四、方法二:双指针正序合并(需要额外数组)
思路
- 创建一个新数组
sorted; - 使用两个指针
p1、p2分别指向nums1和nums2; - 比较两个指针所指的元素,将较小的放入
sorted; - 当某一方到达末尾,直接拷贝另一方剩余元素;
- 最后将
sorted的内容拷回nums1。
代码实现
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int[] sorted = new int[m + n];
int p1 = 0, p2 = 0, p = 0;
while (p1 < m && p2 < n) {
if (nums1[p1] <= nums2[p2]) {
sorted[p++] = nums1[p1++];
} else {
sorted[p++] = nums2[p2++];
}
}
while (p1 < m) sorted[p++] = nums1[p1++];
while (p2 < n) sorted[p++] = nums2[p2++];
System.arraycopy(sorted, 0, nums1, 0, m + n);
}
}

复杂度分析
- 时间复杂度:O(m + n)
- 空间复杂度:O(m + n)
优缺点
- 优点:清晰直观;
- 缺点:使用了额外数组,空间复杂度较高。
五、方法三:双指针逆序合并(最优解)
核心思想
从后往前合并可以避免覆盖问题:
-
设三个指针:
p1 = m - 1指向nums1的最后一个有效元素;p2 = n - 1指向nums2的最后一个元素;p = m + n - 1指向nums1的最后一个位置(总长度)。
-
比较
nums1[p1]与nums2[p2]:- 较大的放到
nums1[p]; - 指针左移;
- 较大的放到
-
重复直到
p2< 0; -
若
nums1剩余部分无需处理;
若nums2还有剩余,拷贝剩下的部分。
图示(示例)
nums1 = [1,2,3,0,0,0]
nums2 = [2,5,6]
初始:
p1=2, p2=2, p=5
比较 3 vs 6 → 6 放到 nums1[5]
nums1 = [1,2,3,0,0,6]
继续比较 3 vs 5 → 5 放到 nums1[4]
nums1 = [1,2,3,0,5,6]
继续比较 3 vs 2 → 3 放到 nums1[3]
nums1 = [1,2,3,3,5,6]
最后将 nums2 剩下的 [2] 放入。
代码实现
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1 = m - 1, p2 = n - 1, p = m + n - 1;
while (p1 >= 0 && p2 >= 0) {
if (nums1[p1] > nums2[p2]) {
nums1[p--] = nums1[p1--];
} else {
nums1[p--] = nums2[p2--];
}
}
// 如果 nums2 还有剩余,拷贝到前面
while (p2 >= 0) {
nums1[p--] = nums2[p2--];
}
}
}

复杂度分析
- 时间复杂度:O(m + n)
- 空间复杂度:O(1)
- 不需要额外空间,完全原地合并。
六、三种方法对比
| 方法 | 思路 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|---|
| 1. 直接合并排序 | 简单粗暴 | O((m+n)log(m+n)) | O(1) | 简洁 | 效率不高 |
| 2. 双指针正序 | 从前往后合并 | O(m+n) | O(m+n) | 逻辑直观 | 需要额外数组 |
| 3. 双指针逆序 | 从后往前合并 | O(m+n) | O(1) | 原地合并,最优 | 稍复杂但经典 |
七、总结
- 核心思想: 充分利用已排序数组的特性;
- 关键技巧: 从尾部开始合并,避免元素覆盖;
- 最优实现: 双指针逆序法(O(m + n) 时间,O(1) 空间)。
857

被折叠的 条评论
为什么被折叠?



