两个排序数组的中位数
给定两个大小为 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
看完题目以后,我很快想到这个题目的算法思路:
1、合并两个有序数组
2、取合并后数组的中位数
并很快写出java实现代码:
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if(nums1.length==0 && nums2.length==0){
return 0;
}else if(nums1.length==0){
return getMiddleNum(nums2);
}else if(nums2.length==0){
return getMiddleNum(nums1);
}
int[] mergeNum = mergeArray(nums1,nums2);
double middleNum = getMiddleNum(mergeNum);
return middleNum;
}
public static int[] mergeArray(int[] nums1, int[] nums2){
int[] mergeNum = new int[nums1.length+nums2.length];
int i=0,j=0,k=0;
while(i<nums1.length && j<nums2.length){
if(nums1[i]<=nums2[j]){
mergeNum[k++] = nums1[i++];
}else{
mergeNum[k++] = nums2[j++];
}
}
if(i<nums1.length){
while(i<nums1.length)
mergeNum[k++]=nums1[i++];
}else{
while(j<nums2.length)
mergeNum[k++]=nums2[j++];
}
return mergeNum;
}
public static double getMiddleNum(int[] mergeNum){
if(mergeNum.length%2==1){ //奇数
return mergeNum[mergeNum.length/2];
}else{
double result = ((double)mergeNum[mergeNum.length/2]+(double)mergeNum[mergeNum.length/2-1])/2;
return result;
}
}
提交以后的执行效率也很慢:
这个速度我真是接受不了啊,仔细想想,为什么非要把两个数组合并起来再找中位数呢?其实完全可以直接查找中位数呀。就是按照合并数组的思路遍历数组,直到遍历到第middle个(middle代表中位数,奇数是middle是一个数,偶数时,middle是两个数求平均)。以下是实现的代码:
public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
int len1 = nums1.length;
int len2 = nums2.length;
int flag = (len1+len2+1)/2; //中位数的下标值
if((len1+len2)%2==1){ //如果合并后的数组长度是奇数:
return getRankNum(flag, nums1, len1, nums2, len2);
}else{
double pre = getRankNum(flag, nums1, len1, nums2, len2);
double sub = getRankNum(flag+1, nums1, len1, nums2, len2);
return (pre+sub)/2;
}
}
public static double getRankNum(int flag, int[] nums1, int len1, int[] nums2, int len2){
double middle = 0;
int count = 1, i=0,j=0;
while(count<=flag){
if(i<len1 && j<len2){
if(nums1[i]<=nums2[j]){
middle = nums1[i];
i++;
}else{
middle = nums2[j];
j++;
}
}else if(i<len1 && j>=len2){
middle = nums1[i];
i++;
}else if(i>=len1 && j<len2){
middle = nums2[j];
j++;
}
count++;
}
return middle;
}
提交以后的执行结果:
提高了一些,还算可以吧。
网上有人用第K小的思路实现了这个问题,代码如下:
public static double findMedianSortedArrays(int[] nums1, int[] nums2){
int len1 = nums1.length;
int len2 = nums2.length;
int size = len1 + len2;
if(size % 2 == 1)
return findKth(nums1, 0, len1, nums2, 0, len2, size/2 + 1); //size代表第几个数
else
return (findKth(nums1, 0, len1, nums2, 0, len2, size/2)+
findKth(nums1, 0, len1, nums2, 0, len2, size/2 + 1))/2;
}
public static double findKth(int[] nums1, int start1, int len1,
int[] nums2, int start2, int len2, int k){
if(len1-start1 > len2-start2)
return findKth(nums2, start2, len2, nums1, start1, len1, k);
if(len1 - start1 == 0)
return nums2[k-1];
if(k == 1)
return Math.min(nums1[start1], nums2[start2]);
//p1,p2记录当前需要比较的那个位
int p1 = start1 + Math.min(len1 - start1, k/2);
int p2 = start2 + k - p1 + start1;
if(nums1[p1-1] < nums2[p2-1])
return findKth(nums1, p1, len1, nums2, start2, len2, k - p1 + start1);
else if(nums1[p1-1] > nums2[p2-1])
return findKth(nums1, start1, len1, nums2, p2, len2, k - p2 + start2);
else
return nums1[p1 -1];
}
执行结果如下:
效率不是很高呀,真不知道前50%的大神们用的是什么思路哇,还得继续研究,希望大家有思路的可以共享一下,我们一起学习呀~~~