0088.合并两个有序数组

难度:🟢 简单

标签数组双指针排序

链接88. 合并两个有序数组

题目描述

给你两个按 非递减顺序 排列的整数数组 nums1nums2,另有两个整数 mn ,分别表示 nums1nums2 中的元素数目。

请你 合并 nums2nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意: 最终,合并后的数组不应由函数返回,而是存储在数组 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) 的时间复杂度。

思路选择

采用 三指针法 是解决此问题的最优策略。

  1. 一个指针 p1 指向 nums1 有效元素部分的末尾(即索引 m-1)。

  2. 一个指针 p2 指向 nums2 的末尾(即索引 n-1)。

  3. 一个指针 p 指向 nums1 数组的最末尾(即索引 m+n-1),这是合并后最大元素应该存放的位置。

通过比较 p1p2 指向的元素大小,将较大的元素放入 p 指向的位置,然后移动相应的指针,如此往复,直到合并完成。

关键步骤

  1. 初始化:初始化三个指针 p1 = m - 1p2 = n - 1p = m + n - 1

  2. 从后向前遍历:当 p2 指针仍然有效时(即 p2 >= 0),持续循环。这个条件是关键,因为只要 nums2 的元素还没被完全合并,我们就需要继续操作。

  3. 比较与放置

    • 在循环中,比较 nums1[p1]nums2[p2] 的值。

    • 情况一:如果 p1 仍有效(p1 >= 0)且 nums1[p1] 大于 nums2[p2],则将 nums1[p1] 放到 nums1[p] 的位置,并将 p1 向前移动。

    • 情况二:否则(即 p1 已越界或 nums2[p2] 更大或相等),将 nums2[p2] 放到 nums1[p] 的位置,并将 p2 向前移动。

  4. 移动插入指针:每放置一个元素后,都需要将插入指针 p 向前移动。

  5. 结束:当 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) 指针 p1p2 总共只会完整地遍历 mn 个元素一次。

  • 空间复杂度O(1) 我们只使用了常数个额外变量作为指针,合并操作是在 nums1 数组内部完成的(in-place),没有使用与输入规模成正比的额外空间。

相关题目

总结

本题是双指针思想的经典应用。通过 逆向双指针 的方法,巧妙地解决了在数组原地合并时可能发生的数据覆盖问题,从而实现了高效且空间占用小的解决方案。理解这种从后向前的操作思路是掌握本题的关键。

Copyright © Jun 2025 all right reserved,powered by Gitbook该文件修订时间: 2025-07-03 17:35:08

results matching ""

    No results matching ""

    results matching ""

      No results matching ""