4. 寻找两个正序数组的中位数
题目描述
给定两个大小分别为
m
和n
的正序(从小到大)数组nums1
和nums2
。请你找出并返回这两个正序数组的 中位数 。
解题思路
有序数组中找中位数,其实就是找特定下标的元素。
简单的思路是先将两个有序数组归并,归并后总元素数量为 m+n
,
- 若总元素数目为奇数,则寻找下标为
(m+n)/2
的元素 - 若元素为偶数,则寻找下标为
(m+n)/2-1
和(m+n)/2
的元素,取平均
该思路的时间复杂度为 O(m+n)
。
如果省去归并操作,直接使用二分法来寻找特定的元素,那么就可以把事件复杂度降至 O(log(m+n))
。
这种思路的关键在于设计二分法的策略。首先,“两个正序数组中寻找中位数”可以转化为“两个正序数组中寻找第k大的数”,然后,使用排除法,一点一点地去掉第1~k-1大的数。
二分法的循环体
比较 nums1[k/2 - 1]
和 nums2[k/2 - 1]
,如果 nums1[k/2 - 1] <= nums2[k/2 - 1]
:
nums2[k/2 - 1] >= nums1[k/2 - 1] >= ... >= nums1[0]
,排除掉k/2
个元素
在下一轮循环中,nums1[0]
、nums1[1]
、…、nums1[k/2 - 1]
被排除,并且 k -= k/2
。
二分法的边界条件
- 如果某数组为空,那么返回另一个数组第k大的数
- 如果
k=1
,那么返回两数组首元素中较小的数
二分法的特殊情况
每轮循环中,我们设置的下标 index + k/2 - 1
可能越界,此时需要把二分法更新迭代的步长设为0。
复杂度分析
时间复杂度:O(log(m+n))
空间复杂度:O(1)
知识点
二分法、中位数
代码
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int size1 = nums1.size();
int size2 = nums2.size();
if ((size1 + size2) % 2 == 1) {
return findKthElement(nums1, nums2, (size1 + size2)/2 + 1);
} else {
cout << findKthElement(nums1, nums2, (size1 + size2)/2) << endl;
cout << findKthElement(nums1, nums2, (size1 + size2)/2 + 1) << endl;
return (findKthElement(nums1, nums2, (size1 + size2)/2)
+ findKthElement(nums1, nums2, (size1 + size2)/2 + 1)) / 2.0;
}
}
double findKthElement(vector<int>& nums1, vector<int>& nums2, int k) {
int begIndex1 = 0;
int begIndex2 = 0;
int size1 = nums1.size();
int size2 = nums2.size();
int step1 = k/2 - 1;
int step2 = k/2 - 1;
while (true) {
if (size1 - begIndex1 == 0) {
return nums2[begIndex2 + k - 1];
} else if (size2 - begIndex2 == 0) {
return nums1[begIndex1 + k - 1];
} else if (k == 1) {
return min(nums1[begIndex1], nums2[begIndex2]);
} else if (begIndex1 + step1 >= size1) {
step1 = 0;
} else if (begIndex2 + step2 >= size2) {
step2 = 0;
}
if (nums1[begIndex1 + step1] <= nums2[begIndex2 + step2]) {
begIndex1 += step1 + 1;
// begIndex2 += step2;
k -= step1 + 1;
} else {
// begIndex1 += step1;
begIndex2 += step2 + 1;
k -= step2 + 1;
}
step1 = k/2 - 1;
step2 = k/2 - 1;
// cout << "begIndex1: " << begIndex1 << endl;
// cout << "begIndex2: " << begIndex2 << endl;
// cout << "k: " << k << endl;
}
}
};