在编程竞赛(Competitive Programming,简称CP)社区中,流传着这样一句自嘲式的灵魂拷问:「How do you mastered CP? Help me solve Median of Two Sorted Arrays」。这道LeetCode第4题、同时也是无数面试和竞赛中的常客,以其精妙的二分思想与边界处理,成为衡量算法思维成熟度的标尺。今天,我们就以这道题为切入点,剖析其解题精髓,并探讨从“刷题”走向“精通CP”的路径。

一、题目回顾:一个看似简单却暗藏玄机的问题

给定两个大小分别为 mn 的正序(从小到大)数组 nums1nums2,要求找出并返回这两个正序数组的中位数,且算法的时间复杂度应为 O(log (m+n))

例如:

  • 输入:nums1 = [1,3], nums2 = [2] → 输出:2.0
  • 输入:nums1 = [1,2], nums2 = [3,4] → 输出:2.5

如果只要求线性复杂度,合并两个有序数组再取中位数即可,但 O(log) 的约束直接排除了这种思路——它暗示我们必须使用二分查找。

二、核心解法:二分切割与边界美学

这道题最经典的解法是将问题转化为“寻找第k小的数”。但更优雅的方式是采用“切割法”(Partition):在两个数组中分别切一刀,将数组分成左右两部分,使得左半部分所有数 ≤ 右半部分所有数,且左右元素个数相等(或左比右多一个)。此时中位数即为左半部分最大值(或左右最大/最小的平均值)。

关键步骤:

  1. 始终对较短的数组进行二分,确保时间复杂度为 O(log min(m,n))
  2. 设切点 inums1 中,j = (m+n+1)/2 - inums2 中。
  3. 确保 nums1[i-1] ≤ nums2[j]nums2[j-1] ≤ nums1[i]。若满足,则找到正确切分;若不满足,根据比较结果调整 i 的二分区间。
  4. 处理边界情况:当切点在数组两端时,用 -∞+∞ 代替缺失元素。

代码实现(Python示例):

def findMedianSortedArrays(nums1, nums2):
    if len(nums1) > len(nums2):
        nums1, nums2 = nums2, nums1
    m, n = len(nums1), len(nums2)
    left, right = 0, m
    while left <= right:
        i = (left + right) // 2
        j = (m + n + 1) // 2 - i
        nums1_left = nums1[i-1] if i > 0 else float('-inf')
        nums1_right = nums1[i] if i < m else float('inf')
        nums2_left = nums2[j-1] if j > 0 else float('-inf')
        nums2_right = nums2[j] if j < n else float('inf')
        if nums1_left <= nums2_right and nums2_left <= nums1_right:
            if (m + n) % 2 == 0:
                return (max(nums1_left, nums2_left) + min(nums1_right, nums2_right)) / 2
            else:
                return max(nums1_left, nums2_left)
        elif nums1_left > nums2_right:
            right = i - 1
        else:
            left = i + 1

三、从一道题到精通CP:思维模式的跃迁

这道题之所以被频繁用于“拷问”CP水平,是因为它综合了多项核心能力: - 二分思想的迁移:不是对单个数组二分,而是对“划分点”二分,要求理解二分本质是“缩减搜索空间”。 - 边界处理严谨性:数组越界、奇偶性、无穷大占位等细节,稍有不慎就会全盘皆输。 - 复杂度优化直觉:必须意识到对长数组二分效率低下,从而选择短数组。

回到标题中的困惑:“How do you mastered CP?” 答案并非刷题数量的堆砌,而是学会从每道题中提炼通用范式。例如,这道题的“切割法”可推广至“在两个有序数组中找第k小数”“在旋转数组中找中位数”等问题。CP高手往往具备“模式识别”能力:看到 O(log) 立刻想到二分,看到两个有序数组立即联想到“分而治之”。

四、给CP学习者的三条建议

  1. 从暴力到优化,理解每一层复杂度:不要跳过暴力解法。先写出O(m+n)的合并,再思考如何用二分优化,这个过程比直接背诵最优解更重要。
  2. 画图、写边界、手算样例:对于这类切割类问题,在纸上画出数组、标注切点,手动模拟二分过程,能显著降低心智负担。
  3. 建立“错题本”与“模板库”:记录自己最初卡住的点(例如为什么用短数组二分?为什么j的公式是那样?),并整理成可复用的代码模板。下次遇到“寻找第k个元素”时,就能快速套用。

结语

「Median of Two Sorted Arrays」并非CP之路上的终点,而是一块试金石。它检验的不仅是二分法的熟练度,更考验一个人面对复杂条件时的冷静与条理。当你能在10分钟内写出无误的代码,并自信地解释为何边界条件成立时,你就离“mastered CP”更近了一步。记住:精通不是记住所有解法,而是拥有从问题本质出发、构建解法体系的能力。从这道题开始,为你的CP之路铺下坚实的基座。