4.寻找两个正序数组的中位数(二分法)
给定两个大小分别为 m
和n
的正序(从小到大)数组nums1
和nums2
。请你找出并返回这两个正序数组的 中位数 。
要求算法的时间复杂度应该为 O(log (m+n)) 。
思路:
一开始就能想到两个简单的思路:1. 将两个数组合并后再排序,计算出中位数的位置,直接返回; 2. 使用两个指针分别指向这两个数组的头部,然后根据计算的中位数位置开始移动当前两个指针指向的值中的小值,直到找到中位数。这个方法涉及到的边界问题很复杂。虽然可以通过,但是时间复杂度不满足要求。
还有种方法为二分法:
寻找中位数其实就是寻找一组数中间位置的数,这样就可以将问题转化为求第k
小元素。k
为中位数的位置。数组长度为奇数或为偶数时,计算方法稍有不同,但是偶数情况的中位数为第k
小和第k+1
小的元素求和再除2
。
这里两个有序数组A
和B
,利用二分法的思想,首先取k=k/2
,比较A[k\2 - 1]
和B[k\2 - 1]
,对于这两个中的较小值,最多只会有
(
k
/
2
−
1
)
+
(
k
/
2
−
1
)
≤
k
−
2
(k/2-1)+(k/2-1) \leq k-2
(k/2−1)+(k/2−1)≤k−2个元素比它小,那么它就不能是第 k小的数了。可以直接排除掉。
这里会出现三种情况:
A[k\2 - 1] > B[k\2 - 1]
,此时排除掉A[0]
到A[k\2 - 1]
的元素,它们都不可能是第k
小的元素。A[k\2 - 1] < B[k\2 - 1]
,此时排除掉B[0]
到B[k\2 - 1]
的元素,它们都不可能是第k
小的元素。A[k\2 - 1] = B[k\2 - 1]
,可以归到第一种情况处理。
这样处理一轮,我们就排除了一些元素,接下来需要减少k
的值(因为我们排除的都是不大于第k
小元素的),在排除后的数组上继续寻找第k
小元素,注意,这里的k值不一定是k-k/2
。有以下三种特殊情况:
- 若
A[k\2 - 1]
或B[k\2 - 1]
越界,可以选择越界数组的最后一个元素和另一个数组作比较,防止程序出错。此时需要按照实际排除的元素个数来减少k的值,不能直接k -= k/2
; - 如果一个数组为空,说明该数组中的所有元素都被排除,我们可以直接返回另一个数组中第 k 小的元素。
- 若
k==1
,返回排除过后两个数组首元素的较小值即为所求。
实际代码中使用两个指针分别指向两个数组,排除的过程其实就是两个指针后移的过程。代码如下:
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int totalLen = nums1.length + nums2.length;
//中位数位置
int k = (int) (totalLen + 1) / 2 ;
//偶数
if(totalLen % 2 == 0) {
double mid = (getMinK(nums1, nums2, k) + getMinK(nums1, nums2, k + 1)) / 2;
return mid;
} else {
//奇数
double mid = getMinK(nums1, nums2, k);
return mid;
}
}
private double getMinK(int[] nums1, int[] nums2, int k){
int len1 = nums1.length, len2 = nums2.length;
int index1 = 0, index2 = 0;
while(true){
//边界情况
//若A数组已经遍历完了,则当前指向B数组的指针再往后移k-1步,就是所求
if(index1 == len1){
return nums2[index2 + k - 1];
}
if(index2 == len2){
return nums1[index1 + k - 1];
}
if(k == 1){
return Math.min(nums1[index1], nums2[index2]);
}
int mid = k / 2;
int newIndex1 = Math.min(len1, index1 + mid) - 1;
int newIndex2 = Math.min(len2, index2 + mid) - 1;
int p1 = nums1[newIndex1], p2 = nums2[newIndex2];
if(p1 <= p2){
k -= (newIndex1 - index1 + 1);
index1 = newIndex1 + 1;
} else {
k -= (newIndex2 - index2 + 1);
index2 = newIndex2 + 1;
}
}
}
}
若哪里总结有误,还望指正!