算法练习-合并两个有序数组

原题链接-合并两个有序数组

题目核心要求

  1. 有两个​​已经排好序(非递减)​ ​ 的数组 nums1nums2

  2. 要把 nums2合并到 nums1里面去。

  3. ​不能新开一个数组​ ​来存结果,必须直接修改 nums1

  4. nums1的长度足够大,后面用 0占好了位置。


方法一:直觉法(先合并,再排序)

这是最容易想到的方法,但​​不是最高效的​​),不过非常适合理解。

​思路:​

  1. 直接把 nums2里的所有元素,按顺序塞到 nums1末尾那些 0的位置上。

  2. 然后对整个 nums1调用 Python 自带的排序函数 sort()

​Python 代码:​

python 复制代码
def merge(nums1, m, nums2, n):
    """
    :type nums1: List[int]
    :type m: int
    :type nums2: List[int]
    :type n: int
    :rtype: None Do not return anything, modify nums1 in-place instead.
    """
    # 1. 把 nums2 的所有元素,从 nums1 的第 m 个位置开始覆盖
    # 例如:m=3, 就从 nums1[3] 开始覆盖后面的0
    nums1[m:m+n] = nums2
    
    # 2. 直接对 nums1 进行原地排序
    nums1.sort()

# 测试一下
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, 3, 5, 6]

​缺点:​

我们没有利用"两个数组都已经排好序"这个条件,直接排序的时间复杂度是 O((m+n) * log(m+n)),不够高效。


方法二:双指针法(从后往前 - 推荐!)

这是​​最优解。它的核心思想是利用已知的"有序"条件,像拉链一样把两个数组合并起来。

​思路比喻:​

假设你有两叠已经从大到小排好序的卡片(nums1的有效部分和 nums2),现在你要把它们合并成一叠,并且只能从最上面(最大的数字)开始比较和放置。

  1. 设置三个"指针":

    • p1:指向 nums1有效部分的最后一个元素(索引 m-1)。

    • p2:指向 nums2的最后一个元素(索引 n-1)。

    • p:指向 nums1数组的最后一个位置(索引 m+n-1),这是用来放最终结果的。

  2. ​从后往前​ ​比较 nums1[p1]nums2[p2]

    • 谁大,就把谁放到 nums1[p]的位置。

    • 然后,对应的指针 (p1p2) 和 p都向前移动一位。

  3. 重复步骤2,直到其中一个数组的所有元素都比较完。

  4. ​重要收尾​ ​:如果最后 nums2里还有剩余的元素(即 p2 >= 0),说明这些元素都是最小的,需要把它们按顺序复制到 nums1最前面的位置。

​为什么要从后往前?​

因为 nums1的后面是空闲的 0,从大到小放置不会覆盖掉 nums1前面还没比较到的有效数字。如果从前往后放,会覆盖数据,需要额外空间。

​Python 代码:​

python 复制代码
def merge(nums1, m, nums2, n):
    # 初始化三个指针
    p1 = m - 1 # 指向 nums1 有效部分的末尾
    p2 = n - 1 # 指向 nums2 的末尾
    p = m + n - 1 # 指向 nums1 整个数组的末尾

    # 当两个数组都还有元素没比较时
    while p1 >= 0 and p2 >= 0:
        if nums1[p1] > nums2[p2]:
            # 如果 nums1 的元素大,把它放到后面
            nums1[p] = nums1[p1]
            p1 -= 1
        else:
            # 如果 nums2 的元素大(或相等),把它放到后面
            nums1[p] = nums2[p2]
            p2 -= 1
        p -= 1 # 每放一个元素,总指针前移一位

    # 收尾:如果 nums2 还有剩余元素(意味着这些是最小的)
    # 直接把它们复制到 nums1 的前面
    # 如果 p2 < 0,说明 nums2 已经全部处理完,这个循环不会执行
    nums1[:p2 + 1] = nums2[:p2 + 1]

# 测试
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, 3, 5, 6]

​为什么最后要收尾?​

来看一个例子:nums1 = [4, 5, 6, 0, 0, 0], nums2 = [1, 2, 3]

  • 按照从后往前比较:

    • 6>3-> 放6

    • 5>3-> 放5

    • 4>3-> 放4

  • 此时 p1已经变成 -1,循环结束。但 nums2里还剩 [1, 2, 3]这三个元素没处理!

  • 所以我们需要 nums1[:p2 + 1] = nums2[:p2 + 1]这行代码,把 nums2剩下的所有元素直接放到 nums1的最开头。

​复杂度分析:​

  • ​时间复杂度:O(m+n)​​。我们只遍历了每个元素一次。

  • ​空间复杂度:O(1)​​。没有使用额外的数组空间,只用了几个变量。

总结

方法 思路 优点 缺点 推荐度
​先合并后排序​ 直接把 nums2放进去,然后调用 sort() 代码极其简单,容易想到 没有利用有序条件,效率低 ⭐⭐
​双指针(从后往前)​ 像拉链一样,从最大的元素开始比较和放置 效率最高,利用了有序条件 思路稍微复杂一点 ⭐⭐⭐⭐⭐

我建议:

  1. 先理解方法一的直觉思路。

  2. 再重点攻克方法二的双指针法,这是算法的核心思想,在很多其他问题中也会用到。

  3. 多画图模拟一下指针移动的过程,就会豁然开朗!

相关推荐
2501_924879261 分钟前
客流特征识别误报率↓76%!陌讯多模态时序融合算法在智慧零售的实战解析
大数据·人工智能·算法·目标检测·计算机视觉·视觉检测·零售
NekoCNN4 分钟前
现代视角下的线性表全解
数据结构
工藤新一¹11 分钟前
C/C++ 数据结构 —— 树(2)
c语言·数据结构·c++·二叉树··c/c++
北京地铁1号线14 分钟前
广告推荐模型2:因子分解机(Factorization Machines, FM)
人工智能·算法·推荐算法
不枯石38 分钟前
Python实现RANSAC进行点云直线、平面、曲面、圆、球体和圆柱拟合
python·计算机视觉
七十二小時1 小时前
力扣热题——前K个高频元素
数据结构·算法·leetcode
站大爷IP1 小时前
Python Lambda:从入门到实战的轻量级函数指南
python
深盾安全1 小时前
Python 装饰器精要
python
站大爷IP1 小时前
Python爬虫基本原理与HTTP协议详解:从入门到实践
python
山烛1 小时前
深度学习:卷积神经网络(CNN)
图像处理·人工智能·python·深度学习·cnn·卷积神经网络