1. 概述

在本篇文章中,我们将讲解如何高效地找出两个已排序数组中的公共元素。这个问题在算法和实际开发中都非常常见,比如在合并数据、查找交集等场景中都有应用。

2. 问题描述

我们有两个升序数组

  • a = [a₁ ≤ a₂ ≤ … ≤ aₙ]
  • b = [b₁ ≤ b₂ ≤ … ≤ bₘ]

我们的任务是找出这两个数组中所有公共元素,并保证输出结果也是升序排列的。数组中允许有重复元素,结果中也要体现重复的次数。

举个例子:

  • a = [1, 2, 2, 4, 5]
  • b = [2, 2, 2, 3, 5, 7]

那么输出应该是:[2, 2, 5]

3. 线性时间解法(双指针法)

✅ 思路

既然两个数组都是有序的,我们可以使用双指针法来线性扫描两个数组,逐个比较当前元素:

  • 如果 a[i] < b[j],说明 a[i] 不可能是公共元素,移动 i
  • 如果 a[i] > b[j],同理,移动 j
  • 如果相等,说明找到一个公共元素,加入结果数组,并同时移动 ij

这种方法类似于归并排序中“合并”两个数组的步骤。

✅ 伪代码

algorithm LinearAlgorithmForCommonElements(a, b):
    c <- an empty array
    i <- 1
    j <- 1

    while i <= n and j <= m:
        if a[i] < b[j]:
            i <- i + 1
        else if b[j] < a[i]:
            j <- j + 1
        else:
            append a[i] to c
            i <- i + 1
            j <- j + 1
            
    return c

✅ 时间复杂度

  • **O(n + m)**:每个数组只被遍历一次,效率非常高

✅ 示例演示(图解)

初始状态:

Common elements - step 1

逐步比较:

  • a[0] = 1 < b[0] = 2 → i++
  • a[1] = 2 == b[0] → 加入结果,i++, j++
  • a[2] = 2 == b[1] → 加入结果,i++, j++
  • ...
  • 最终结果:[2, 2, 5]

4. 对数时间解法(二分查找 + 双指针优化)

⚠️ 适用场景

当其中一个数组远小于另一个时,比如 m << n,我们可以考虑对较小数组中的每个元素,在较大数组中做二分查找

✅ 优化思路

  • 遍历数组 b 中的每个元素 b[j]
  • a 执行二分查找,判断 b[j] 是否存在
  • 同时利用 a 是有序数组的特性进行优化:每次查找后可以将查找起点后移,避免重复搜索前面的元素

✅ 伪代码

algorithm FindCommonElementsBinarySearch(a, b):
    c <- an empty array
    k <- 1
    j <- 1

    while j <= m and k <= n:
        k <- BINARY-SEARCH(b[j], a[k:n])
        
        if b[j] == a[k]:
            append b[j] to c
            k <- k + 1
        else:
            if j < m and b[j+1] > b[j]:
                k <- k + 1
        
        j <- j + 1

    return c

✅ 时间复杂度

  • 最坏情况:O(m log n)
  • 如果 m 远小于 n,可以近似看作 **O(log n)**,效率非常高
  • 如果 m ≈ n,则为 **O(n log n)**,虽然不如线性算法,但比暴力解法好很多

5. 总结对比

方法 时间复杂度 适用场景 优点 缺点
暴力解法(双重循环) O(n * m) 无排序信息 简单直接 性能差
双指针法(线性扫描) O(n + m) 两数组都排序 高效、简单 仅适用于有序数组
二分查找法 O(m log n) 其中一个数组远小于另一个 高效 实现略复杂,需注意查找起点优化

建议:优先使用双指针法,除非你知道其中一个数组特别小,才考虑使用二分查找法。


💡 踩坑提醒

  • 不要忽略重复元素的处理
  • 不要忘记边界条件判断(如指针越界)
  • 二分查找时注意起始位置优化,否则可能退化为 O(m log n) 的低效版本

🔚 总结

两个有序数组找公共元素是一个非常经典的算法问题。使用双指针法可以在 O(n + m) 时间内高效解决,是首选方案。在特定条件下(如小数组查找),二分查找法也能发挥优势。掌握这两种方法,能帮助你在实际开发和算法面试中游刃有余。


原始标题:How to Find Common Elements in Two Sorted Arrays