面试知识库

4. 寻找两个正序数组的中位数#

今天我们要挑战一道 LeetCode 难度系数最高的题目之一 - LeetCode 4「寻找两个正序数组的中位数」。这道题不仅考察我们对二分查找的理解,更要求我们具备深入的数学思维。让我们一起攻克这个难题!

📚 从生活场景切入#

想象你是一位体育老师,手里有两份不同班级的体测成绩单,都已经按照分数排序。现在你需要找出这两个班级所有同学成绩的中间水平。直观的做法是把两个成绩单合并后重新排序,但这样效率太低。其实我们可以用更聪明的方法,这就是今天要讨论的问题。

问题描述#

题目目标#

给定两个大小分别为 m 和 n 的正序数组 nums1 和 nums2 ,请你找出并返回这两个正序数组的中位数。题目要求算法的时间复杂度为 O(log(m + n))。

示例 1#

输入: nums1 = [1,3], nums2 = [2] 输出: 2.00000 说明: 合并后为 [1,2,3],中位数为 2

💡 问题解析#

题目要求: 给定两个有序数组 nums1 和 nums2,要求找出这两个数组的中位数。要求算法的时间复杂度为 O(log(m+n))。

示例:

// 示例1:总长度为奇数
// 输入:nums1 = [1,3], nums2 = [2]
// 输出:2.0
// 解释:合并后为 [1,2,3],中位数是中间那个数 2

// 示例2:总长度为偶数
// 输入:nums1 = [1,2], nums2 = [3,4]
// 输出:2.5
// 解释:合并后为 [1,2,3,4],中位数是中间两个数 (2 和 3) 的平均值
java

🤔 思路发展历程#

1. 直观思路(不符合要求)#

最简单的方法是合并两个数组后找中位数,但时间复杂度是 O(m+n),不满足题目要求。

2. 优化思路#

我们可以转化思路:不需要真正合并数组,只需要找到第 (m+n)/2 小的元素(或者在偶数情况下找第 (m+n)/2 和 (m+n)/2+1 小的元素)。这启发我们使用二分查找来定位这个位置。

🚀 优雅的解决方案#

📝 代码详解#

让我们深入理解这个解决方案的精妙之处:

1. 核心思想#

我们要找的是一个位置,将两个数组分别分成左右两部分,使得:

  • 左半部分的元素都小于右半部分
  • 左半部分的元素个数等于右半部分(或比右半部分多1个)

2. 关键步骤解析#

A. 预处理

  • 确保 nums1 是较短的数组,这样可以减少搜索范围
  • 计算两个数组的总长度,确定中位数的位置

B. 二分查找过程

  • 在较短的数组 nums1 中寻找分割线位置 i
  • 根据 i 计算 nums2 中的分割线位置 j
  • 比较分割线两侧的元素大小关系

C. 边界处理

  • 使用 Integer.MIN_VALUE 和 Integer.MAX_VALUE 处理边界情况
  • 分别处理总长度为奇数和偶数的情况

🎯 易错点剖析#

  1. 数组长度处理

    • 必须确保在较短的数组上进行二分查找
    • 正确处理奇偶数长度的情况
  2. 边界条件

    • 分割线在数组边缘时的处理
    • 两个数组长度差距较大时的情况
  3. 二分查找的条件

    • 正确判断分割线是否合适
    • 准确调整搜索范围

💡 举一反三#

这道题的思路可以扩展到其他场景:

  1. 找第k小的数

    • 可以用类似的二分思想
  2. 合并有序数组

    • 理解分割线的概念有助于优化合并过程
  3. 数据流的中位数

    • 类似的思想可以应用到动态数据中

🎨 图解算法#

为了更好地理解这个算法,让我们通过可视化来看看它是如何工作的:

🌟 面试技巧#

  1. 思路表达

    • 先说明暴力解法的局限
    • 解释为什么需要用二分查找
    • 清晰描述如何找到分割线
  2. 复杂度分析

    • 解释为什么时间复杂度是 O(log(min(m,n)))
    • 说明空间复杂度是 O(1)
  3. 代码优化

    • 展示对边界情况的处理
    • 说明如何使代码更简洁高效

这道题是 LeetCode 难度最高的题目之一,但通过我们的详细讲解,相信大家已经理解了它的核心思想。记住,解决复杂问题的关键在于:将大问题拆分成小问题,找到突破口,然后逐步优化解决方案。如果你对这个题目还有任何疑问,欢迎在评论区讨论!