LeetCode 4. Median of Two Sorted Arrays
问题描述
There are two sorted arrays nums1 and nums2 of size m and n respectively.
Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
You may assume nums1 and nums2 cannot be both empty.
输入输出实例
Example 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
题解
先瞄一眼时间复杂度O(log(m+n))
,嗯没错应该是用二分法了,如果是只有一个数组的话,我想大家看到这里会锤我一把骂我浪费大家时间了。这题是在两个已排序的数组中,找到两个数组中所有元素的中位数,也就是说需要考虑奇数和偶数个元素的情况。那我们不妨把问题简化一下,寻找两个数组中的Kth
元素如何?在这个题K=(m+n+1)/2
和K=(m+n+2)/2
,我们不妨验证一下:(这里的/
是C
中的整数除法运算哦)
- 假设
len(nums1)=2
和len(nums2)=1
,那么K=2
。 - 假设
len(nums1)=2
和len(nums2)=2
,那么K=2和3
。
那怎么在两个有序数组中寻找Kth
元素呢?
我们先找到两个数组中(K/2)th
元素,分别为a
和b
,分以下情况讨论:
-
a < b
,那就代表着nums1
中前K/2
个元素一定不存在中位数。 -
a > b
,那就代表着nums2
中前K/2
个元素一定不存在中位数。 -
a = b
,那我们好像就得到答案了?
也就是说,我们每次都淘汰K/(2^k)
个元素(k是我们处理的次数),然后把剩余的元素进行上述操作,即递归操作。有的同学看到这里要开始怼我了,说两个数组不一定有K/2
个元素你怎么说?那我们可以这样处理嘛(先把刀放下),假设第一个数组元素小于K/2
,我们可以让a=INT_MAX
,为什么呢?同样道理嘛,如果len(nums1)<K/2
,就代表nums2
的前K/2
个元素一定不存在中位数,为什么?反证法走起!要让其存在中位数,则len(nums1)+K/2 > K
,做不到吧?
现在说说边界条件,什么时候应该停止呢?
- 当你把某个数组淘汰完后,也就是你只需要在剩下的数组上,使用
numsx[begin+K/(2^(k-1))]
找到它了。 - 否则,当
K/(2^(k-1)) == 1
时,意味着我们只需要比较两个数组段(因为我们把数组不断地切割)的首元素就好,找到最小的那个!
到这里我们把findKth
描述完了,最后只需要比较一下K1
和K2
,如果相等我们就没必要找两次了嘛!
废话不多说,上代码!(希望大家好好想想数组定位的下标为什么这么写?因为我的表达能力有限,无法给你们说出个所以然)
Code
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int m1 = (nums1.size() + nums2.size() + 1) / 2,
m2 = (nums1.size() + nums2.size() + 2) / 2;
if (m1 == m2) return findKthNum(nums1, 0, nums2, 0, m1);
else return (findKthNum(nums1, 0, nums2, 0, m1) + findKthNum(nums1, 0, nums2, 0, m2)) / 2.0;
}
private:
int findKthNum(vector<int>& nums1, int i, vector<int>& nums2, int j, int k) {
if (i >= nums1.size()) return nums2[j + k - 1];
if (j >= nums2.size()) return nums1[i + k - 1];
if (k == 1) return min(nums1[i], nums2[j]);
int mid1 = i + k / 2 - 1 < nums1.size() ? nums1[i + k / 2 - 1] : INT_MAX;
int mid2 = j + k / 2 - 1 < nums2.size() ? nums2[j + k / 2 - 1] : INT_MAX;
if (mid1 < mid2) {
return findKthNum(nums1, i + k / 2, nums2, j, k - k / 2);
} else {
return findKthNum(nums1, i, nums2, j + k / 2, k - k / 2);
}
}
};
复杂度
上述题解中我们也说到,每次我们都淘汰K/(2^k)
个元素,虽然我们有m+n
个元素,但事实上如果K < m+n
,我们接触到的数组范围实际上是K
呀,(这里我不太确定),但是我们算法的上界确实是O(log(m+n))
。