每日LeetCode:合并两个有序数组

在数据处理和算法设计中,有序数组合并是一个基础而重要的问题。本文将带你深入剖析 LeetCode 经典题目「合并两个有序数组」,逐步优化解法。

问题描述

给定两个按 非递减顺序 排列的整数数组 nums1nums2

  • nums1 长度为 m + n,前 m 个元素有效,后 n 个元素为占位值 0
  • nums2 长度为 n
  • 需将 nums2 合并到 nums1 中,使合并后数组保持 非递减顺序
  • 要求 :直接修改 nums1,不返回新数组
css 复制代码
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]

解法一:暴力合并后排序

核心思想:忽略数组有序特性,直接合并后排序。

算法步骤

  1. nums2 复制到 nums1 尾部
  2. 调用内置排序函数排序
javascript 复制代码
const merge = (nums1, m, nums2, n) => {
    // 复制 nums2 到 nums1 尾部
    for (let i = 0; i < n; i++) {
        nums1[m + i] = nums2[i];
    }
    // 原地排序
    nums1.sort((a, b) => a - b);
};

时间复杂度分析

  • 复制操作:O(n)
  • 排序操作:O((m+n)log(m+n))
  • 总时间复杂度:O((m+n)log(m+n))

空间复杂度:O(1)(原地排序)

缺点:未利用数组有序特性,排序开销大

解法二:正向双指针(额外空间)

核心思想:利用双指针遍历有序数组,使用临时数组存储结果

算法步骤

  1. 创建临时数组 temp
  2. 初始化双指针 p1=0(nums1),p2=0(nums2)
  3. 比较指针值,取较小值放入 temp
  4. 处理剩余元素
  5. 复制 tempnums1
javascript 复制代码
const merge = (nums1, m, nums2, n) => {
    // 创建临时数组
    const temp = new Array(m + n);
    let p1 = 0, p2 = 0, t = 0;
    
    // 合并有序部分
    while (p1 < m && p2 < n) {
        if (nums1[p1] <= nums2[p2]) {
            temp[t++] = nums1[p1++];
        } else {
            temp[t++] = nums2[p2++];
        }
    }
    
    // 处理 nums1 剩余
    while (p1 < m) temp[t++] = nums1[p1++];
    
    // 处理 nums2 剩余
    while (p2 < n) temp[t++] = nums2[p2++];
    
    // 复制回 nums1
    for (let i = 0; i < m + n; i++) {
        nums1[i] = temp[i];
    }
};

时间复杂度:O(m+n)(遍历所有元素一次)

空间复杂度:O(m+n)(需要额外空间)

缺点:空间复杂度不满足最优要求

知识点扩展:双指针技巧 双指针是处理有序数组的高效技巧,常见模式:

  1. 同向指针:解决滑动窗口问题
  2. 相向指针:解决两数之和等问题
  3. 快慢指针:解决链表环检测问题

解法三:逆向双指针(最优解)

核心思想 :利用 nums1 尾部空闲空间,从后向前填充较大值

算法步骤

  1. 初始化三指针:
    • p1 = m-1(nums1 有效末尾)
    • p2 = n-1(nums2 末尾)
    • p = m+n-1(合并位置)
  2. 从后向前比较并填充
  3. 处理 nums2 剩余元素
javascript 复制代码
const merge = (nums1, m, nums2, n) => {
    let p1 = m - 1;    // nums1 有效部分末尾
    let p2 = n - 1;    // nums2 末尾
    let 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. 逆向操作:避免覆盖未比较元素
  2. 尾部空闲空间:利用 nums1 的预留空间
  3. 剩余处理:只需处理 nums2 剩余元素

为什么不需要处理 nums1 剩余?

  • 当 nums2 元素处理完后,nums1 剩余元素已处于正确位置
  • 这些元素是全局最小值且位置正确

算法比较总结

解法 时间复杂度 空间复杂度 核心优势 适用场景
暴力排序 O((m+n)log(m+n)) O(1) 实现简单 快速原型开发
正向双指针 O(m+n) O(m+n) 利用有序特性 无空间限制场景
逆向双指针 O(m+n) O(1) 原地操作,空间最优 内存敏感环境

合并有序数组看似简单,却蕴含着分治思想、双指针技巧和空间优化策略。掌握这一基础算法,将为处理更复杂的有序数据问题奠定坚实基础。

相关推荐
KaMeidebaby9 分钟前
卡梅德生物技术快报|bli亲和力检测gst:告别批量跑胶:BLI实时酶切监测技术加速GST融合蛋白下游流程优化
前端·网络·数据库·人工智能·算法
bloglin9999926 分钟前
langchain 和 langgraph 和 react
javascript·react.js·langchain
触底反弹29 分钟前
🚀 删了数据刷新又回来?3 组件 × 4 回调 × 3 坑讲透 React 父子通信
前端·javascript·react.js
alphaTao30 分钟前
LeetCode 每日一题 2026/7/20-2026/7/26
算法·leetcode
会周易的程序员2 小时前
js-shm: 高性能 Node.js 共享内存模块
开发语言·javascript·c++·node.js·共享内存·shm
带娃的IT创业者3 小时前
重新定义前端构建速度:深度解析 SWC 如何用 Rust 颠覆 JavaScript 工具链
前端·javascript·rust·前端构建·swc
新中地GIS开发老师4 小时前
零基础WebGIS开发入门 | GeoJSON数据持久化
前端·javascript·gis·webgis·三维gis开发
青山木4 小时前
Hot 100 ---腐烂的橘子
java·数据结构·后端·算法·leetcode·广度优先
未来之窗软件服务4 小时前
计算机考试-快速排序—东方仙盟
数据结构·算法·排序算法·仙盟创梦ide·东方仙盟
blns_yxl4 小时前
正则驱动实时表单验证(HTML+CSS+JS+正则)
javascript·css·html