【Leetcode】两个有序数组的中位数
来源:互联网 发布:python中import用法 编辑:程序博客网 时间:2024/05/22 15:34
解法一:
当合并后的总元素个数是奇数时,中位数的下标是n/2。当总元素个数是偶数时,中位数是下标n/2-1和下标n/2两个元素的平均值。不论总个数奇偶,可以将n/2作为右中位数,n/2-1作为左中位数,只不过总个数是奇数时,没用到左中位数。也就是说必须要找到第n/2+1个元素。
private static double findMedian(int[] A,int[] B){ if(A==null||B==null) return -1; int m=A.length; int n=B.length; int len=m+n; if(len==0) return -1; if(n==0) return findMedian(B,A); int left=-1,right=-1; int aStart=0,bStart=0; for(int i=0;i<=len/2;i++){ left=right; if(aStart<m&&(bStart>=n||A[aStart]<B[bStart])){ right=A[aStart++]; }else{ right=B[bStart++]; } } if((len&1)==0) return (left+right)/2.0; else return right; }
上面的方法还可以推广到更普遍的情况,找出两个有序数组合并后的第k个元素。
private static int findKth(int[] A,int[] B,int k){ if(A==null||B==null) return -1; int m=A.length; int n=B.length; int len=m+n; if(len<k||len==0) return -1; if(m==0) return B[k-1]; if(n==0) return A[k-1]; int c=0; int aStart=0,bStart=0; int result=-1; while(c<k){ if(aStart<m&&(bStart>=n||A[aStart]<B[bStart])){ result=A[aStart++]; }else{ result=B[bStart++]; } c++; } return result; }
解法二:
解法一的实质是每次剔除一个不可能是中位数的元素。其实也可以利用二分查找的思想每次剔除大范围元素提高搜索效率。因为查找中位数实质上是查找第k个元素。
对于数组A前p个元素A[0]、A[1]、A[2]、……A[p-1],有p-1个元素不大于A[p-1],同样数组B前k-p个元素B[0]、B[1]、B[2]、……B[k-p-1],有k-p-1个元素不大于B[k-p-1]。比较A[p-1]和B[k-p-1]存在三种情况:
1、若A[p-1]=B[k-p-1],那么共有p-1+(k-p-1)+1个元素不大于A[p-1],也就是说A[p-1]即是第k小的数。
2、若A[p-1]< B[k-p-1],那么第k小的数不可能在区间A[0,p)之内。我们可以用反证法证明,若第k小的数在A[0,p)中设为A[i],那么A[i]前面只有i个数不比它大,不妨设A[p-1]是第k小的数即i=p-1,也就是说最多只有p-1个数不比它大,而B[k-p-1]大于A[p-1],所以B[k-p-1]之后的所有数都大于A[p-1],这样即使B[p-k-1]之前的所有数都不比A[p-1]大,B中也只能找到k-p-1个数不比A[p-1]大。那么A和B中最多只有p-1+(k-p-1)=k-2个数不大于A[p-1],从而A[p-1]不是第k小的数。
这里要注意推论第k小的数不在某区间的前提是同一数组中元素的相对位置不变,只有之前的数不比某个数大,之后的数不论是否相等都比它“大”。如122234中第2个2只有两个数1、2不比它大,第3个2不算在内。否则,第k小的数可能出现在任何位置,如A={1,1,1,1,1},B={1,1,1,1,1}。 但是即使认为第k小的数可以出现在任何位置,上面的推论也没有错,因为除了区间A[0,p)之外必定会出现一个第k小,而我们只需要找到一个就可以了,因此舍弃掉区间A[0,p)是可行的。如A={0,1,1}和B={0,1,2,4,5,6}找第5小的数字,舍弃掉A(0,1)仍然可以找到解1。
同样我们可以通过反证法证明,B[k-p-1]之后的数不可能是第k小的数,因为它至少是B[k-p],前面有k-p个数不比B[k-p]大,再加上A[0,p)有p个数不比B[k-p]大,所以至少有k-p+p=k个数不比B[k-p-1]大,也就是说B[k-p]至少是第k+1小的数而不可能是第k小的数。 舍弃区间之后问题就变成寻找第k-p小的数了。
3、同理若A[p-1]>B[k-p-1]可以舍弃掉B[0,p)区间和A[p-1]之后的区间。
public static double findMedianSortedArrays(int A[], int B[]) { if(A==null||B==null) return -1.0; int m=A.length; int n=B.length; int len=m+n; if((len&1)==0){ return (findK(A,0,m-1,B,0,n-1,len/2)+findK(A,0,m-1,B,0,n-1,len/2+1))/2.0; }else{ return findK(A,0,m-1,B,0,n-1,len/2+1); } } //k表示第k小,对应下标为k-1 public static int findK(int[] A,int startA,int endA,int[] B,int startB,int endB,int k){ int lenA=endA-startA+1; int lenB=endB-startB+1; if(lenA<=0&&lenB>0){ return B[startB+k-1]; }else if(lenB<=0&&lenA>0){ return A[startA+k-1]; } if(lenA>lenB) { return findK(B,startB,endB,A,startA,endA,k); } if(k==1) { return Math.min(A[startA],B[startB]); } int ka=Math.min(lenA,k/2); int kb=k-ka; if(A[startA+ka-1]==B[startB+kb-1]){ return A[startA+ka-1]; }else if(A[startA+ka-1]<B[startB+kb-1]){ return findK(A,startA+ka,endA,B,startB,startB+kb-1,k-ka); }else{ return findK(A,startA,startA+ka-1,B,startB+kb,endB,k-kb); } }
编程时没有注意整数/2结果是取商不是小数,下标越界、数组首指针习惯写成0、A[startA+ka-1]偷懒复制成A[startB+kb-1]却忘记将A改成B等问题结果调试了很久。
- [LeetCode]4 两个有序数组的中位数
- 【Leetcode】两个有序数组的中位数
- [LeetCode] 求两个有序数组的中位数
- 两个有序数组的中位数
- 两个有序数组的中位数
- 两个有序数组的中位数
- 两个有序数组的中位数
- 两个有序数组的中位数
- 两个有序数组的中位数
- 两个有序数组的中位数
- 求两个有序数组的中位数
- 求两个有序数组的中位数
- 求两个有序数组的中位数
- 求两个有序数组的中位数
- 求解两个有序数组的中位数
- 求两个有序数组的中位数
- 找两个有序数组的中位数
- 求两个有序数组的中位数
- numpy tile(瓦片)函数用法
- Jdk1.8中的LinkHashMap实现原理
- beaglebone black 开发常用网站
- Java8中聚合操作collect、reduce方法详解
- SlidingMenu
- 【Leetcode】两个有序数组的中位数
- NYOJ - 118 - 修路方案 ( 次小生成树 )
- 【java】Arrays.asList、Arrays.copyOfRange
- iOS9及以下的自带词典无法下载问题的解决方法
- 不要苛求他人伟大
- HTML <form> novalidate 属性
- CSS3(一)
- Huffman编码树的C++实现
- 基于LL(1)文法实现的简单计算器