当前位置: 首页 > news >正文

合并两个有序数组的高效算法详解

合并两个有序数组的高效算法详解

  • **合并两个有序数组的高效算法详解**
    • **1. 问题描述**
    • **2. 常见解法分析**
      • **方法 1:合并后排序(暴力法)**
      • **方法 2:双指针法(额外空间)**
    • **3. 最优解法:双指针从后向前合并**
      • **核心思路**
      • **代码实现**
      • **复杂度分析**
      • **示例演示**
    • **4. 关键点总结**
    • **5. 扩展思考**
    • **6. 总结**


合并两个有序数组的高效算法详解

1. 问题描述

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

要求:

  • nums2 合并到 nums1 中,使合并后的数组仍然保持 非递减顺序
  • nums1 的初始长度为 m + n,其中后 n 个元素为 0,表示应被 nums2 填充的位置。

示例

输入:
nums1 = [1, 2, 3, 0, 0, 0], m = 3  
nums2 = [2, 5, 6], n = 3输出:
[1, 2, 2, 3, 5, 6]

2. 常见解法分析

方法 1:合并后排序(暴力法)

思路

  1. nums2 的所有元素直接放入 nums1 的末尾。
  2. 对整个 nums1 进行排序。

代码

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)(未使用额外空间)

缺点

  • 没有利用 nums1nums2 已经有序 的特性,导致时间复杂度较高。

方法 2:双指针法(额外空间)

思路

  1. 创建一个临时数组 temp,用两个指针分别遍历 nums1nums2,按顺序选择较小的元素放入 temp
  2. 最后将 temp 复制回 nums1

代码

public void merge(int[] nums1, int m, int[] nums2, int n) {int[] temp = new int[m + n];int i = 0, j = 0, k = 0;while (i < m && j < n) {if (nums1[i] <= nums2[j]) {temp[k++] = nums1[i++];} else {temp[k++] = nums2[j++];}}while (i < m) temp[k++] = nums1[i++];while (j < n) temp[k++] = nums2[j++];System.arraycopy(temp, 0, nums1, 0, m + n);
}

复杂度分析

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

缺点

  • 需要额外的 O(m+n) 空间,不符合题目对空间复杂度的优化要求。

3. 最优解法:双指针从后向前合并

核心思路

由于 nums1 的后面有足够的空间(填充 0),我们可以 从后向前 合并,避免覆盖 nums1 前面的数据。

步骤

  1. 初始化三个指针:
    • i = m - 1nums1 有效部分的末尾)
    • j = n - 1nums2 的末尾)
    • k = m + n - 1nums1 最终合并后的末尾)
  2. 比较 nums1[i]nums2[j],将较大的数放入 nums1[k],并移动指针。
  3. 如果 nums2 还有剩余元素,直接复制到 nums1 前面。

代码实现

public void merge(int[] nums1, int m, int[] nums2, int n) {int i = m - 1, j = n - 1, k = m + n - 1;while (j >= 0) {  // 只需要处理 nums2 剩余元素if (i >= 0 && nums1[i] > nums2[j]) {nums1[k--] = nums1[i--];} else {nums1[k--] = nums2[j--];}}
}

复杂度分析

  • 时间复杂度:O(m + n)(只需遍历 nums1nums2 各一次)
  • 空间复杂度:O(1)(原地修改,无额外空间)

示例演示

输入

nums1 = [1, 2, 3, 0, 0, 0], m = 3  
nums2 = [2, 5, 6], n = 3

执行过程

步骤ijknums1[i]nums2[j]操作nums1 结果
122536nums1[5] = 6[1,2,3,0,0,6]
221435nums1[4] = 5[1,2,3,0,5,6]
320332nums1[3] = 3[1,2,3,3,5,6]
410222nums1[2] = 2[1,2,2,3,5,6]
51-11--j < 0 退出循环合并完成

最终结果

[1, 2, 2, 3, 5, 6]

4. 关键点总结

  1. 为什么从后向前合并?

    • nums1 后面有足够的空间,不会覆盖未处理的元素。
    • 如果从前向后合并,需要额外空间存储被覆盖的数据。
  2. 为什么 while (j >= 0) 就够了?

    • 只要 nums2 合并完成,nums1 剩余部分已经在正确位置,无需处理。
  3. 边界情况处理

    • nums1 为空 → 直接复制 nums2
    • nums2 为空 → nums1 保持不变。

5. 扩展思考

  • 如果 nums1nums2 不是有序的,如何优化?
    → 先排序再合并,但时间复杂度会上升至 O((m+n) log(m+n))

  • 如果要求返回新数组而不是修改 nums1
    → 可以用 方法 2(双指针 + 额外空间)


6. 总结

方法时间复杂度空间复杂度适用场景
合并后排序O((m+n) log(m+n))O(1)简单但效率低
双指针(额外空间)O(m+n)O(m+n)需要新数组时
双指针(原地合并)O(m+n)O(1)最优解

推荐使用双指针从后向前合并,既高效又节省空间! 🚀

http://www.xdnf.cn/news/470143.html

相关文章:

  • 多级分类的实现方式
  • Xinference推理框架
  • 遗传算法求解旅行商问题分析
  • Python内存管理:赋值、浅拷贝与深拷贝解析
  • Mendix 连接 MySQL 数据库
  • Linux动态库热加载驱动插件机制-示例
  • 国标GB28181视频平台EasyGBS助力智慧医院打造全方位视频监控联网服务体系
  • QML元素 - MaskedBlur
  • 力扣-236.二叉树的最近公共祖先
  • Elasticsearch 常用语法手册
  • 格恩朗椭圆齿轮流量计 工业流量测量的可靠之钥
  • MySQL库的操作
  • 【笔记】CosyVoice 模型下载小记:简单易懂的两种方法对比
  • vacuum、vacuum full的使用方法及注意事项
  • “禁塑行动·我先行”环保公益项目落地宁夏,共筑绿色生活新篇章
  • 4、前后端联调文生文、文生图事件
  • 趋势跟踪策略的回测
  • AI Agent开发第67课-彻底消除RAG知识库幻觉-文档分块全技巧(1)
  • pgsql14自动创建表分区
  • SpringBoot 自动装配流程
  • [Java实战]Spring Boot 3实现 RBAC 权限控制(二十五)
  • SpringBoot项目使用POI-TL动态生成Word文档
  • 去年开发一款鸿蒙Next Os的window工具箱
  • 软考软件评测师——软件工程之系统维护
  • ADS1220高精度ADC(TI)——应用 源码
  • 采用sherpa-onnx 实现 ios语音唤起的调研
  • 每周靶点:NY-ESO-1、GPC3、IL27分享
  • Linux操作
  • Oracle APEX IR报表列宽调整
  • [ctfshow web入门] web75