方法一:合并两个数组,然后求中位数,时间复杂度为:O(m+n)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int length1 = nums1.length;
int length2 = nums2.length;
int[] result = new int[length1 + length2];
int i = 0,j = 0,k = 0;
while (i < length1 && j < length2){
if (nums1[i] < nums2[j]){
result[k] = nums1[i];
i++;
}else {
result[k] = nums2[j];
j++;
}
k++;
}
while (i < length1){
result[k] = nums1[i];
k++;
i++;
}
while (j < length2){
result[k] = nums2[j];
k++;
j++;
}
if (k % 2 == 0){
return (result[k/2] + result[k/2 - 1]) / 2.0;
}else {
return result[k/2];
}
}
}
方法二:分治法
一、中位数的作用
将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。
二、双数组划分
对数组A、B分别在和
处进行划分,并且将A和B的左半部分放入到left_part集合当中,把A和B的右半部分放入到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]
根据上边的划分可以推出:
是偶数
是奇数 (左边比右边多一个数)
那么中位数也就可以得到:
是偶数
是奇数
要想得到上边的结论,条件有两个:
(或者:
)
如果
,只需要
,
并且
为了简化分析:假设总是存在的;具体情况放在后边讨论。
为什么要求成立? 是为了确保j的值不为负数。
三、明确需求
说了这么多,我们需要做的就是在中找到目标对象
,使得以下条件成立:
并且
,其中
四、怎么去找
采用二分查找。
1.设 ,然后开始在
中进行搜索。
2.令
3.现在;那么有三种情况需要说明:
并且
,这个条件就是搜索结束的标记。
,说明
划分的太小了,需要把
向后移动,以便增大
;所以新的搜索范围就变为:
。
,说明
划分的太大了,需要把i向前移动,以便减小
;所以新的搜索范围就变为:
4.当搜索范围改变时,继续循环判断即可。
当找到目标对象时,中位数为:
,当
为奇数的时候
,当
为偶数的时候
五、处理边界值
当,此时
可能发生越界。但是通过分析可以得到,只要
不越界,
就不会越界。
越界就是发生在重新确定和
的时候,就是在
或者
之前判断一下当前
是否在
内。
六、代码
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2){
int m = nums1.length;
int n = nums2.length;
//一定要保证n≥m成立
if (m > n){
//交换两个数组
int[] temp = nums1;
nums1 = nums2;
nums2=temp;
//交换m和n
int temp2 = m;
m = n;
n = temp2;
}
int imin = 0,imax = m,half = (m + n + 1) / 2;
while (imin <= imax){
int i = (imin + imax) / 2;
int j = half - i;
if (i > imin && nums1[i-1] > nums2[j]){
//nums1[i]太大了,需要小一点
imax = i - 1;
}else if (i < imax && nums2[j-1] > nums1[i]){
//nums1[i]太小了,需要大一点
imin = i + 1;
}else {
//nums1[i]符合划分
//找左边的最大值
int leftMax = 0;
if (i == 0){
leftMax = nums2[j-1];
}else if (j == 0){
leftMax = nums1[i-1];
}else {
leftMax = Math.max(nums1[i-1], nums2[j-1]);
}
if ((m + n) % 2 == 1){
return leftMax;
}
//找右边的最小值
int rightMin = 0;
if (i == m){
rightMin = nums2[j];
}else if (j == n){
rightMin = nums1[i];
}else {
rightMin = Math.min(nums1[i],nums2[j]);
}
return (leftMax + rightMin) / 2.0;
}
}
return 0.0;
}
}