LeetCode 4:寻找两个正序数组的中位数 ------ 二分查找法 题解
🔗 题目链接
👉 https://leetcode.cn/problems/median-of-two-sorted-arrays/
📖 题目概要
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出并返回这两个正序数组的中位数。
要求 :算法的时间复杂度应该为 O(log(m + n))。
示例 1 :
输入:nums1 = 1,3, nums2 = 2
输出:2.00000
解释:合并数组 = 1,2,3,中位数 2
示例 2 :
输入:nums1 = 1,2, nums2 = 3,4
输出:2.50000
解释:合并数组 = 1,2,3,4,中位数 (2 + 3) / 2 = 2.5
✅ 本题解法:二分切割法(最优解,AC代码)
核心思想
- 不合并数组 ,直接在两个数组上做二分查找
- 将两个数组左右切割,让左半部分整体 ≤ 右半部分
- 通过切割位置直接算出中位数
- 始终把短数组作为 nums1,减少二分次数
💡 详细解题思路
1. 核心原理:数组切割
把两个数组各分成左右两部分:
nums1左:[0, i-1],右:[i, m-1]nums2左:[0, j-1],右:[j, n-1]
满足两个条件:
- 左半总长度 = 右半总长度(或多1)
i + j = m - i + n - j + 1
→ 推导得:j = (m + n + 1) / 2 - i - 左边所有数 ≤ 右边所有数
nums1[i-1] ≤ nums2[j]且nums2[j-1] ≤ nums1[i]
2. 边界处理
- 左边没数:用
Integer.MIN_VALUE - 右边没数:用
Integer.MAX_VALUE
3. 中位数计算
- 总长度奇数:左边最大值就是中位数
- 总长度偶数:(左边最大值 + 右边最小值) / 2.0
4. 二分调整切割点
nums1[i-1] > nums2[j]:i太大 → 左移r = i - 1nums2[j-1] > nums1[i]:i太小 → 右移l = i + 1
✅ AC 代码
java
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if(nums1.length > nums2.length) {
int[] tmp = nums1;
nums1 = nums2;
nums2 = tmp;
}
int m = nums1.length; //元素少的数组
int n = nums2.length;
int l = 0, r = m;
while(l <= r) {
int i = (l + r) / 2;
int j = (m+n+1) / 2 - i; //绳子总长减去i的长度
int l1 = (i==0) ? Integer.MIN_VALUE : nums1[i-1];
int r1 = (i==m) ? Integer.MAX_VALUE : nums1[i];
int l2 = (j==0) ? Integer.MIN_VALUE : nums2[j-1];
int r2 = (j==n) ? Integer.MAX_VALUE : nums2[j];
// 满足条件
if (l1<=r2 && l2<=r1) {
if((m+n) % 2 == 1) {
return Math.max(l1,l2);
} else {
return (Math.max(l1,l2) + Math.min(r1,r2)) / 2.0;
}
} else if(l1 > r2) {
r = i - 1;
} else {
l = i + 1;
}
}
return 0;
}
}
⏱️ 复杂度分析
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(log(min(m,n))) | 只对短数组二分 |
| 空间复杂度 | O(1) | 只用常数变量 |
📌 核心一句话总结
在两个有序数组上做二分切割,让左边整体 ≤ 右边,满足长度平衡后,直接从边界值计算中位数。
📝 高频面试问答
1. 为什么要把短数组放前面?
让二分次数变少,保证时间复杂度 O(log(min(m,n)))。
2. 为什么 j 这么算?
j = (m + n + 1) / 2 - i
保证左右两边长度相等或左边多1,方便取中位数。
3. 为什么要用 MIN/MAX 处理边界?
防止数组越界,同时不影响大小比较。
最后记录一下,第一遍刷完Leetcode的Hot100题。虽然有一些题是抄题解的。
