LeetCode No4 寻找两个正序数组的中位数
作者:互联网
题目
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-10^6 <= nums1[i], nums2[i] <= 10^6
思路
暴力解法
如果不考虑时间复杂度的要求,这道题可以用暴力去解决,根据中位数的定义,中位数一定是有序序列的中间一位或者两位的平均值,那么我只要同时开始遍历两个数组,找到中间的两个数,然后根据两个数组长度总和的奇偶,奇数的话就取小值,偶数就取两者的平均数。
二分查找
暴力的解法固然可以AC,但是还是不满足题目中对于时间复杂度的要求,而且对于这种在有序的数组中找数的题目,第一反应都是用二分去做,只是这题比较特殊,是要在两个数组里面求合并后的值,当然我们可以合并数组后再二分查找,但事实上,既然暴力解法我们都不需要真正的合并数组,那使用二分查找的时候肯定也可以不用。
于是我们再看下中位数的定义:
中位数(Median)又称中值,统计学中的专有名词,是按顺序排列的一组数据中居于中间位置的数,代表一个样本、种群或概率分布中的一个数值,其可将数值集合划分为相等的上下两部分。
既然中位数可以将数值集合划分为相等的两部分,那么我们先在一个数组里面看中位数,将数组从中间分割开来,中位数一定在坐标i和i-1两个数中,如果是数组长度为奇数那么取min(nums[i-1], nums[i]),而如果是偶数则为 (nums[i-1]+nums[i])/2。
1 3 5 | 8(i) 9 2 4 6 | 7(i) 10 12
同样如果是两个数组,将两个数组从中间分隔开,使得左边的数据个数=右边的数据个数或者左边比右边多一个,那这个时候基本上就能确定中位数的位置了
1 3 5 | 8(i) 9 2 4 6 | 7(j) 10 12
当然仅仅只是数量上满足条件当然还不够,如果中位数就在分割线附近的话,那么分割线附近的4个数,在合并两个数组后应该是一个连续字串,也就是需要满足
nums1[i-1]<=nums2[j] && nums1[j-1]<=nums2[i]
这个条件,于是我们二分查找的条件也就出来了,但是还是需要注意如下四种边界情况。
1 2 3(i) | | 4(j) 5 6 7
| 4(i) 5 6 1 2 3 4(j) |
1 2 3(i) | | 4(j) 5 6
| 4(i) 5 6 1 2 3(j) |
AC代码
暴力解法
点击查看代码
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length;
int m = nums2.length;
boolean flag = (n+m)%2==0;
int mid = (n+m)/2+1;
int i = 0, j = 0;
int a = 0, b = 0;
int cnt = 1;
while( i<n || j < m ) {
a = b;
if( i!=n ) {
if( j!=m ) {
if( nums1[i] < nums2[j] ) {
b = nums1[i];
i ++;
} else {
b = nums2[j];
j ++;
}
} else {
b = nums1[i];
i ++;
}
} else {
b = nums2[j];
j ++;
}
if( cnt == mid ) {
break;
}
cnt ++;
}
if( flag ) {
return (a+b)/2.0;
}
return b;
}
}
二分查找
点击查看代码
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length;
int m = nums2.length;
if( n > m ) {
return findMedianSortedArrays(nums2, nums1);
}
int left = 0;
int rigth = n;
int i = 0;
int j = 0;
// 定义的分割线需要满足 nums1[i-1]<=nums2[j] && nums1[j-1]<=nums2[i]
// 1 3 5 | 8(i) 9
// 2 4 6 | 7(j) 10 12
// 左边数字个数应该为 (n+m+1)/2 = i + j
while( left<rigth ) {
i = (left + rigth +1 ) / 2;
j = (n+m+1)/2-i;
// 二分确定能满足条件的分割线
if( nums1[i-1]>nums2[j] ) {
rigth = i - 1;
} else {
left = i;
}
}
i = left;
j = (n+m+1)/2-i;
// 边界处理
int numsI1 = i==0? Integer.MIN_VALUE: nums1[i-1];
int numsI = i==n? Integer.MAX_VALUE: nums1[i];
int numsJ1 = j==0? Integer.MIN_VALUE: nums2[j-1];
int numsJ = j==m? Integer.MAX_VALUE: nums2[j];
if( (n+m)%2 == 1 ) {
return Math.max(numsI1, numsJ1);
} else {
return (Math.max(numsI1, numsJ1) + Math.min(numsI, numsJ)) * 0.5;
}
}
}
标签:正序,int,中位数,nums1,length,数组,No4,LeetCode,nums2 来源: https://www.cnblogs.com/Asimple/p/16120589.html