题目说明
给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0
示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
解题思路
计算出两个数组的中位值k,k 位置的值就是中位数。
只要找到中位值k 就可以得出中位数了。
首先我们要理解中位值如何得出:
- 当数组个数为奇数值时:k = (k + 1) / 2;
- 当数组个数为偶数时,k 有两个:k_1 = k / 2, K_2 = k / 2 + 1;
那么中位数为:
- 奇数:k 的值;
- 偶数:(k_1 + k_2) / 2.0;
如何得出两个数组中的中位值:
因为数组是有序数组,所以通过不断的将数组分成两份,排除掉小的那份,逐渐向中位值靠拢。
- num1 数组先被排除完,此时中位值在num2 中,返回此时数组中的k 值;
- num2 数组先被排除完,此时中位值在num1 中,返回此时数组中的k 值;
- 找到k 值,返回两个数组中此时的首元素的最小值。
index 偏移量用来重新定义数组的首元素,而k 值则是当前数组的中位值。
若当前没有匹配到合适的中位值,则将k 值减去删除的元素数量,增加删除数组的偏移量。
// 给出其中较小值
int min(int num1, int num2){
if(num1 <= num2)
return num1;
else
return num2;
}
// 得出中位值k 的值
double findMedianElement(int* nums1, int nums1Size, int* nums2, int nums2Size, int k){
int index1 = 0, index2 = 0; // 用来记录nums1 和 nums2 数组的偏移量
while(true){
// 除非符合条件,否则无限循环
/* 边界情况 */
// nums1 或nums2 为空数组或者刚好排除完时,直接返回另一个数组的中位值
if(index1 == nums1Size)
return nums2[index2 + k - 1];
if(index2 == nums2Size)
return nums1[index1 + k - 1];
// k == 1 时,找到中位值,返回中位值的值。
if(k == 1)
return min(nums1[index1], nums2[index2]);
/* 正常情况 */
// 通过不断的二分来排除掉较小的数组,直到找到中位值为止
// 若超出数组边界,则将数组最后一位位置赋值给newIndex
// 否则newIndex = 偏移量 + 中位值的中位值
int newIndex1 = min(index1 + k / 2 - 1, nums1Size - 1);
int newIndex2 = min(index2 + k / 2 - 1, nums2Size - 1);
// 将当前中位值的值提出
int pivot1 = nums1[newIndex1];
int pivot2 = nums2[newIndex2];
// 对比中位值,将较小的值的偏移量增加,中位值k 减去删除的个数。
if(pivot1 <= pivot2){
k -= newIndex1 - index1 + 1;
index1 = newIndex1 + 1;
}
else{
k -= newIndex2 - index2 + 1;
index2 = newIndex2 + 1;
}
}
}
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
int k = nums1Size + nums2Size;
// 若总数为奇数,直接提出中位数
if(k % 2 == 1)
return findMedianElement(nums1, nums1Size, nums2, nums2Size, (k + 1) / 2);
// 若总数为偶数,找出两个中位值,中位数为两数平均值
else
return (findMedianElement(nums1, nums1Size, nums2, nums2Size, k / 2) + findMedianElement(nums1, nums1Size, nums2, nums2Size, k / 2 + 1)) / 2.0;
}