There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
题意:
两个排序后的数组nums1 和nums2,长度分别是m,n,找出其中位数,并且时间复杂度:O(log(m+n))
最愚蠢的方法:
两个数组合并成一个,排序,取出中位数
Java程序:
public class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; int median_index = (m+n)/2; int nums[] = new int[m+n]; if(m!=0) for(int k=0;k<m;k++) nums[k] = nums1[k]; if(n!=0) for(int k=0;k<n;k++) nums[m+k] = nums2[k]; Arrays.sort(nums); if((m+n)%2==1) return nums[median_index]; else return (nums[median_index]+nums[median_index-1])/2.0; } }
无耻的又用Python实现了一遍
class Solution(object): def findMedianSortedArrays(self, nums1, nums2): """ :type nums1: List[int] :type nums2: List[int] :rtype: float """ m = len(nums1) n = len(nums2) median_index = (m+n)/2 nums=[num for num in nums1] for num in nums2: nums.append(num) nums.sort() if((m+n)%2==1): return nums[median_index] else: return (nums[median_index]+nums[median_index-1])/2.0
说明一下:
Python中 sorted(list)排序是直接返回排序后的结果,原list没有改变,而list.sort() 不返回排序后的结果,结果更新list
正确方法:
问题转化为:nums1和nums2合并排序后的第k个元素
主要思想是:每次剔除部分对求第k个元素没有用的数据
假设nums1和nums2原始序列是升序的,则是求合并后升序的第k个元素,或者第k大元素
假设nums1和nums2原始序列是降序的,则是求合并后降序的第k个元素,或者第k小元素
下面假设是升序
m = nums1.length
n = nums2.length
若 nums1是空的,结果就是 nums2[k]
若 nums2是空的,结果就是 nums1[k]
若 k==0,返回nums1[0] 、nums2[0]中的较小者
对于其他情况:
为了好说明,定义变量:nums1Start,nums1End,nums2Start,nums2End
我们要求的是合并后的第k大的数
小于第k大的数是没有用的
对于合并排序后的前k个数,理想情况下,是均匀的分布在nums1 和nums2中
k个数中nums1中占:nums1Mid = nums1Len*k/(nums1Len+nums2Len)个 这里除法取的是下界
k个数中nums2中占:nums2Mid = k – nums1Mid – 1 个 这里是因为下标是从0开始
由于我们定义了开始位置,则:
nums1Mid = nums1Mid + nums1Start
nums2Mid = nums2Mid + nums2Start
下面比较这两个位置对应元素的大小:nums1[nums1Mid ] nums2[nums2Mid]
若:nums1[nums1Mid ] > nums2[nums2Mid] 说明:第k大的数在nums1中的nums1Start 到nums1Mid 之间 和 nums2 中nums2Mid 到nums2End之间
可以发现我们去除的有效部分只是nums2中的nums2Start 到 nums2Mid之间的元素,而nums1中 nums1Mid到nums1End之间的原始已经是大于 第k个元素了。
所以更新k,k = k - (nums2Mid – nums2Start + 1)
同时更新:nums1End = nums1Mid ,nums2Start = nums2Mid + 1,其他不变
若:nums1[nums1Mid ] < nums2[nums2Mid] 说明:第k大的数在num1中的num1Mid到nums1End之间 和 nums2中的nums2Start到nums2Mid之间
更新k,k = k - (nums1Mid – nums1Start + 1)
同时更新: nums1Start = nums1Mid + 1,nums2End = nums2Mid ,其他不变
若:nums1[nums1Mid ] = nums2[nums2Mid]
这里正好是第k个数,就是所求答案
对于上面两种情况,继续递归求解
关于时间复杂度:在最坏的情况下,每次都要去除k/2的元素,则log(k) = log((m+n)/2) = log(m+n)
Java程序:
public class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int m = nums1.length; int n = nums2.length; if((m+n)%2==1)//odd return (double)findKth(nums1,0,m-1,nums2,0,n-1,((m+n)/2)); else return (findKth(nums1,0,m-1,nums2,0,n-1,(m+n)/2)+findKth(nums1,0,m-1,nums2,0,n-1,(m+n)/2-1))/2.0; } public int findKth(int[] nums1,int nums1Start,int nums1End,int[] nums2,int nums2Start,int nums2End,int k){ int nums1Len = nums1End - nums1Start + 1; int nums2Len = nums2End - nums2Start + 1; if(nums1Len ==0) return nums2[nums2Start+k]; if(nums2Len ==0) return nums1[nums1Start+k]; if(k==0) return nums1[nums1Start]<nums2[nums2Start] ? nums1[nums1Start]:nums2[nums2Start]; int nums1Mid = nums1Len*k/(nums1Len+nums2Len); int nums2Mid = k - nums1Mid - 1 ; nums1Mid = nums1Mid + nums1Start; nums2Mid = nums2Mid + nums2Start; if(nums1[nums1Mid] > nums2[nums2Mid]){ k = k - (nums2Mid - nums2Start + 1); nums1End = nums1Mid; nums2Start = nums2Mid + 1; }else if(nums1[nums1Mid] < nums2[nums2Mid]){ k = k - (nums1Mid - nums1Start + 1); nums2End = nums2Mid; nums1Start = nums1Mid + 1; }else return nums1[nums1Mid]; return findKth(nums1,nums1Start,nums1End,nums2,nums2Start,nums2End,k); } }
对于降序的情况:
当是降序的时候,出来这个结果:
在网上找个C++程序,当是降序的时候,上面的输入也是2.0000,上面给的期望答案也是2.0000
然后我就试试这个输入
上面的C++程序和上面的Java程序都是输入6.0000
题目中没有说升序的啊,但是许多人都默认升序做了,降序输入结果就不对了。
上面的Java程序和上面C++程序原理是很类似的,但是只能对升序求解,降序数组下标需要修改,大于和小于 和上面的恰好相反
好吧,咱就当升序的数组处理。上面的都对。
既然是升序,尝试用两个变量指向两个数组的开始位置,比较大小,异步前进,发现,,,,,,,,,,,,,,,,坑太大,检测中间点的可能太多,,,,,或者定义许多boolean变量检测 ,,,,,,,,,,,不写了,,是写的不对。。。已乱。。。
又转换成Python代码:
class Solution(object): def findMedianSortedArrays(self, nums1, nums2): """ :type nums1: List[int] :type nums2: List[int] :rtype: float """ nums1Len = len(nums1) nums2Len = len(nums2) if (nums1Len + nums2Len)%2==1: return 1.0*self.findKth(nums1,0,nums1Len-1,nums2,0,nums2Len-1,(nums1Len + nums2Len)/2) return (self.findKth(nums1,0,nums1Len-1,nums2,0,nums2Len-1,(nums1Len + nums2Len)/2)+self.findKth(nums1,0,nums1Len-1,nums2,0,nums2Len-1,(nums1Len + nums2Len)/2-1))*0.5 def findKth(self,nums1,nums1Start,nums1End ,nums2,nums2Start,nums2End,k): nums1Len = nums1End - nums1Start + 1 nums2Len = nums2End - nums2Start + 1 if nums1Len==0 : return nums2[nums2Start + k] if nums2Len==0 : return nums1[nums1Start + k] if k==0: return min(nums1[nums1Start],nums2[nums2Start]) nums1Mid = nums1Len*k/(nums1Len + nums2Len) nums2Mid = k - nums1Mid - 1 nums1Mid += nums1Start nums2Mid += nums2Start if nums1[nums1Mid]== nums2[nums2Mid]: return nums2[nums2Mid] if nums1[nums1Mid] > nums2[nums2Mid]: k = k - (nums2Mid - nums2Start + 1) nums1End = nums1Mid nums2Start = nums2Mid + 1 if nums1[nums1Mid] < nums2[nums2Mid]: k = k - (nums1Mid - nums1Start + 1) nums2End = nums2Mid nums1Start = nums1Mid + 1 return self.findKth(nums1,nums1Start,nums1End,nums2,nums2Start,nums2End,k)