题目
给定两个大小分别为m
和n
的有序数组(从小到大),求这两个数组中的中位数。
例如:
- nums1=[1,3];nums2=[2] 中位数为2.0000
- nums1=[1,3];nums2=[2,4] 中位数为(2+3)/2=2.5000
- nums1=[0,0];nums2=[0,0] 中位数为0
- nums1=[];nums2=[1] 中位数为1
我的题解
-
1.将这两个数组合并成一个
有序数组
,合并后计算其中位数
。这种做法需要遍历两个数组,并且合成后的数组可能非常大,感觉效率低下 -
2.我们知道两个数组的长度,那么就知道了中位数的位置n,而且这两个数组是有序的,那么我们遍历两个数组找到第n个大的数就行了。有如下几个问题:
- 如何确定中位数的位置?如果数组个数为奇数,那么中位数的位置就是
lenth/2.0(向上取整)
,如果数组个数为偶数,那么中位数的位置就是length/2.0和lengrh/2.0+1
,可见不管怎样都要用到length/2.0
这个数据。
- 如何确定中位数的位置?如果数组个数为奇数,那么中位数的位置就是
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
//1.计算中位数的位置
double location=Math.ceil((nums1.length+nums2.length)/2.0);
//2.定义计数器和指针
int count=0,i=0,j=0,val1=0,val2=0;
double mid1=0,mid3=0;
if(nums1.length==0||nums2.length==0){
if(nums1.length==0){
mid1=nums2[(int)location-1];
if(location<nums2.length)
mid3=nums2[(int)location];
}
if(nums2.length==0){
mid1=nums1[(int)location-1];
if(location<nums1.length)
mid3=nums1[(int)location];
}
if((nums1.length+nums2.length)%2==0){
return (mid1+mid3)/2;
}else{
return mid1;
}
}else{
while(i<nums1.length||j<nums2.length){
val1=nums1[i];
val2=nums2[j];
if(i<nums1.length&&val1<val2){
i++;
}else if(j<nums2.length&&val1>=val2){
j++;
}
count++;
//判断是否找到中位数
if(count==location){
mid1=val1<val2?val1:val2;
break;
}
}
}
// return mid1;
if((nums1.length+nums2.length)%2==0){
//偶数
double mid2=0;
if(i<nums1.length&&j<nums2.length){
val1=nums1[i];
val2=nums2[j];
mid2=val2>val1?val1:val2;
}else if(i<nums1.length){
mid2=nums1[i];
}else if(j<nums2.length){
mid2=nums2[j];
}
//return 10;
return (mid1+mid2)/2;
}else{
//奇数
return mid1;
}
}
}
写了一年,最后还是没通过,怎么说呢 多多少少的还是有问题。不想解决了
直接看官方题解吧
官方题解
解法一:简单粗暴:合并后找中位数
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int m=nums1.length,n=nums2.length;
//处理有一个数组长度为0的情况
if(m==0){
if(n%2==0){
//偶数
return (nums2[n/2]+nums2[n/2-1])/2.0;
}else{
//奇数
return nums2[n/2];
}
}
if(n==0){
if(m%2==0){
//偶数
return (nums1[m/2]+nums1[m/2-1])/2.0;
}else{
//奇数
return nums1[m/2];
}
}
//处理两个数组长度都不为0的情况
int i=0,j=0;
int count=0;
int nums[]=new int[m+n];
//合并两个数组
while(count!=m+n){
//如果有一个数组已经遍历完了
if(i==m){
while(j!=n){
nums[count++]=nums2[j++];
}
break;
}
if(j==n){
while(i!=m){
nums[count++]=nums1[i++];
}
break;
}
if(nums1[i]<nums2[j]){
nums[count++]=nums1[i++];
}else{
nums[count++]=nums2[j++];
}
}
if((m+n)%2==0){
//偶数
return (nums[(m+n)/2]+nums[(m+n)/2-1])/2.0;
}else{
//奇数
return nums[(m+n)/2];
}
}
}
解法二:解法一的优化
- 我们并不需要对数组全部都进行合并,我们只需要找到中位数在哪里就可以了。
- 最开始的思路肯定就是写一个循环,然后判断是否到了中位数的位置,到了就返回结果,但是这里对偶数和奇数的分类会很麻烦。当其中一个数组遍历完之后,除了
for
循环对边界的判断也会分几种情况。 - 首先是怎么将奇数和偶数的情况合并一下。用
len
表示合并后数组的长度,如果是奇数,我们需要知道第(len+1)/2
个数就可以了,如果遍历的话需要遍历int(len/2)+1
次。如果是偶数,我们需要知道第len/2
和len/2+1
个数,也是需要遍历len/2+1
次。所以遍历的话,奇数和偶数都是len/2+1
次。
返回中位数的话,奇数只需要最后一次遍历的结果就可以了,偶数需要最后一次和上一次遍历的结果。所以我们用两个变量right
和left
,right
保存当前循环的结果,再每次循环之前将right
的值赋给left
。这样在最后一次循环的时候,left
将得到right
的值,也就是上一次循环的结果,接下来right
更新为最后一次的结果。 - 循环该怎么写?什么时候
A
数组后移,什么时候数组B
后移。用aStart
和bStart
分别表示当前指向A
数组和B
数组的位置。如果aStart
还没有到最后并且此时A
位置数字小于B
位置的数字,那么就可以往后移了。也就是aStart<m&&A[aStart]<B[bStart]
。
但是如果B
数组此时已经没有数字了,继续取B[bStart]
,则会越界,所以判断下bStart
是否大于数组长度了,这样||
后边的就不会再执行了,这样也不会导致错误了,所以增加aStart<m&&(b>=n||A[aStart]<B[bStart])
。
class Solution {
public double findMedianSortedArrays(int[] A, int[] B) {
int n=A.length,m=B.length;
int length=n+m;
int aStart=0,bStart=0;
int left=-1,right=-1;
while(aStart+bStart<length/2+1){
left=right;
if(aStart<n&&(bStart>=m||A[aStart]<B[bStart])){
right=A[aStart++];
}else{
right=B[bStart++];
}
}
//计算中位数
if((length&1)==0){
//偶数位
return (left+right)/2.0;
}else{
return right;
}
}
}