问题网址:https://leetcode.com/problems/median-of-two-sorted-arrays/description/
问题描述:
给定两个有序数组nums1和nums2大小分别为m和n。
找到两个有序数组的中位数。 整体运行时间复杂度应为O(log(m + n))。
问题样例:
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
一个简单想法:
学过归并排序的同学显然知道这是一个归并的过程
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
vector<int> a;
int i = 0, j = 0;
while (i < nums1.size() && j < nums2.size()) {
if (nums1[i] <= nums2[j])
a.push_back(nums1[i++]);
else
a.push_back(nums2[j++]);
}
while (i < nums1.size())
a.push_back(nums1[i++]);
while (j < nums2.size())
a.push_back(nums2[j++]);
if (a.size() % 2)
return a[a.size() / 2];
else
return (a[a.size() / 2 - 1] + a[a.size() / 2]) / 2.0;
}
时间复杂度:O(m + n)
改进的想法:
为了解决这个问题,我们需要了解“中位数的应用”。 统计学中,中位数用于:
将集合划分为两个相等长度的子集,一个子集总是大于另一子集。
如果我们了解中位数的使用,我们非常接近答案。
首先让我们将A随机分为两部分:
left_A | right_A
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
由于A有m个元素,所以有m + 1种分法(i = 0~m)。
我们知道:
len(left_A) = i, len(right_A) = m−i.
当 i = 0, left_A 为空, 当 i = m, right_A 为空.
以相同的方式将B在随机分成两部分:
left_B | right_B
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
将left_A和left_B放入一个集合,并将right_A和right_B放入另一集合。 我们把它们命名为left_part和right_part:
left_part | right_part
A[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]
B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]
如果我们能确保:
1. len(left_part)=len(right_part)
2. max(left_part)≤min(right_part)
那么我们将{A,B}中的所有元素分成两个等长的部分,一部分总是大于另一部分。 然后
median = (max(left_part) + min(right_part)) / 2
为了确保这两个条件,我们只需要确保:
i + j = m − i + n − j (or: m - i + n - j + 1)
如果 n ≥ m, 令 i = 0∼m, j = (m+n+1)/2 − i
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
if (nums1.size() > nums2.size())
swap(nums1, nums2);
int m = nums1.size();
int n = nums2.size();
int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
while (iMin <= iMax) {
int i = (iMin + iMax) / 2;
int j = halfLen - i;
if (i < iMax && nums2[j-1] > nums1[i])
iMin++;
else if (i > iMin && nums1[i-1] > nums2[j])
iMax--;
else {
int maxLeft = 0;
if (i == 0)
maxLeft = nums2[j-1];
else if (j == 0)
maxLeft = nums1[i-1];
else
maxLeft = max(nums1[i-1], nums2[j-1]);
if ((m+n) % 2 == 1)
return maxLeft;
int minRight = 0;
if (i == m)
minRight = nums2[j];
else if (j == n)
minRight = nums1[i];
else
minRight = min(nums2[j], nums1[i]);
return (maxLeft + minRight) / 2.0;
}
}
return 0.0;
}
时间复杂度:O(log(m + n))