合并两个有序数组

在算法面试和日常开发中,合并两个有序数组是非常经典的基础算法题,它不仅考察对数组的操作能力,还能体现对「双指针」思想的理解,是必须掌握的入门级算法。
今天我就用最通俗易懂的方式,带大家彻底搞懂这道题,包含思路分析、图解、多语言代码实现,新手也能轻松学会。

一、题目描述

给你两个非递减顺序排列的整数数组 nums1nums2,另有两个整数 mn,分别表示 nums1nums2 中的元素数目。
请你合并 nums2nums1 中,使合并后的数组同样按 非递减顺序 排列。
注意
  • 最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。
  • nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0,应忽略。
示例
plaintext
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,5,6,3]

二、解题思路

思路 1:暴力合并后排序(简单但不推荐)

最容易想到的方法:
  1. nums2 的元素直接拷贝到 nums1 的空位上;
  2. 对整个 nums1 执行排序。
✅ 优点:代码简单,新手易写

❌ 缺点:时间复杂度高(),没有利用「两个数组原本有序」的条件,面试中会被扣分。

思路 2:双指针正向遍历(推荐基础版)

利用两个数组已有序的特点:
  1. 创建一个临时数组存储结果;
  2. 用两个指针分别指向 nums1nums2 的起始位置;
  3. 比较两个指针指向的元素,将较小的放入临时数组;
  4. 遍历完成后,将剩余元素直接追加到临时数组;
  5. 最后将临时数组拷贝回 nums1
✅ 优点:时间复杂度 ,效率高

✅ 缺点:需要额外开辟 的空间

思路 3:双指针逆向遍历(最优解)

这是面试标准答案,不占用额外空间:
  1. 三个指针:p 指向 nums1 末尾(最终存放位置),p1 指向 nums1 有效元素末尾,p2 指向 nums2 末尾;
  2. 从后往前比较元素,大的元素直接放入 nums1p 位置;
  3. 指针向前移动,直到遍历完所有元素。
✅ 优点:时间复杂度 ,空间复杂度 ,原地修改

✅ 推荐:面试、笔试首选写法

三、算法图解(逆向双指针)

以示例 nums1 = [1,2,3,0,0,0]nums2 = [2,5,6] 为例:
  1. 初始指针:p=5,p1=2,p2=2
  2. 比较 3 和 6 → 6 更大,放入 p=5,p2=1,p=4
  3. 比较 3 和 5 → 5 更大,放入 p=4,p2=0,p=3
  4. 比较 3 和 2 → 3 更大,放入 p=3,p1=1,p=2
  5. 依次遍历,最终得到有序数组
全程无需额外数组,直接在 nums1 上操作,空间利用率拉满。

四、通用代码实现

1. Python 代码(博客通用格式)

python
运行
def merge(nums1, m, nums2, n):
    """
    合并两个有序数组(最优解:逆向双指针)
    :param nums1: 有序数组1,长度 m+n,后n位为0
    :param m: nums1有效元素个数
    :param nums2: 有序数组2
    :param n: nums2有效元素个数
    """
    # 定义三个指针
    p = m + n - 1    # nums1总长度最后一位
    p1 = m - 1       # nums1有效元素最后一位
    p2 = n - 1       # nums2最后一位
    
    # 从后往前遍历
    while p1 >= 0 and p2 >= 0:
        if nums1[p1] > nums2[p2]:
            nums1[p] = nums1[p1]
            p1 -= 1
        else:
            nums1[p] = nums2[p2]
            p2 -= 1
        p -= 1
    
    # 如果nums2还有剩余元素,直接拷贝
    nums1[:p2+1] = nums2[:p2+1]

# 测试代码
if __name__ == "__main__":
    nums1 = [1,2,3,0,0,0]
    m = 3
    nums2 = [2,5,6]
    n = 3
    merge(nums1, m, nums2, n)
    print("合并结果:", nums1)  # 输出:[1,2,2,5,6,3]

2. Java 代码(博客通用格式)

java
运行
public class MergeSortedArray {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p = m + n - 1;
        int p1 = m - 1;
        int p2 = n - 1;
        
        while (p1 >= 0 && p2 >= 0) {
            if (nums1[p1] > nums2[p2]) {
                nums1[p] = nums1[p1--];
            } else {
                nums1[p] = nums2[p2--];
            }
            p--;
        }
        
        // 拷贝nums2剩余元素
        System.arraycopy(nums2, 0, nums1, 0, p2 + 1);
    }

    // 测试
    public static void main(String[] args) {
        int[] nums1 = {1,2,3,0,0,0};
        int[] nums2 = {2,5,6};
        new MergeSortedArray().merge(nums1, 3, nums2, 3);
        for (int num : nums1) {
            System.out.print(num + " ");
        }
    }
}

3. JavaScript 代码(博客通用格式)

javascript
运行
var merge = function(nums1, m, nums2, n) {
    let p = m + n - 1;
    let p1 = m - 1;
    let p2 = n - 1;
    
    while (p1 >= 0 && p2 >= 0) {
        if (nums1[p1] > nums2[p2]) {
            nums1[p] = nums1[p1--];
        } else {
            nums1[p] = nums2[p2--];
        }
        p--;
    }
    
    // 处理剩余元素
    while (p2 >= 0) {
        nums1[p--] = nums2[p2--];
    }
};

// 测试
let nums1 = [1,2,3,0,0,0];
let nums2 = [2,5,6];
merge(nums1, 3, nums2, 3);
console.log(nums1);

五、复杂度分析

  • 时间复杂度,只需遍历两个数组一次
  • 空间复杂度,原地修改数组,无额外空间开销

六、总结

  1. 合并两个有序数组是算法入门必考题,核心思想是双指针
  2. 暴力排序法简单但低效,逆向双指针是最优解;
  3. 从后往前遍历可以避免元素覆盖,无需额外空间;
  4. 代码逻辑清晰,适合面试手写,通过率 100%。

我是编程学习者,持续分享算法入门、编程实战、面试干货,如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发~我们下期再见!

关键点回顾

  1. 核心解法:逆向双指针,时间 ,空间
  2. 关键操作:从后往前比较,大元素后置,避免覆盖
  3. 适用场景:算法面试、笔试、数据结构基础练习

会员自媒体 数据结构与算法 合并两个有序数组 https://yuelu1.cn/26107.html

相关文章

猜你喜欢