【转】 两个有序数组中的中位数和Top K问题
原文链接:原文链接
代码
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int n = nums1.size();
int m = nums2.size();
if(n > m) { //保证数组1一定最短,每次都在小的上二分
return findMedianSortedArrays(nums2,nums1);
}
//为了省去奇数偶数的麻烦,
//为每个数组首尾以及两数之间虚加#
//例如: # 2 # 4 # 5 # 7 #
//因此一共有2m+2n+2个元素
//因此数量数组元素数量翻倍
//L1表示数组1 二分后左边最大元素,R1表示数组1右边最大元素
//L2,R2同理
//c1 c2 表示数组1和2分割点位置,也是左侧元素的数量
//lo和hi是数组1的左边和右边的下标
int L1,L2,R1,R2,c1,c2,lo = 0, hi = 2*n;
while(lo <= hi) {
c1 = (lo + hi) / 2; //c1是二分的结果
c2 = m + n - c1; //理解难点,见下面补充
//看下面的注释补充
L1 = (c1 == 0) ? INT_MIN : nums1[(c1-1)/2];
R1 = (c1 == 2*n) ? INT_MAX : nums1[c1/2];
L2 = (c2 == 0) ? INT_MIN : nums2[(c2-1)/2];
R2 = (c2 == 2*m) ? INT_MAX : nums2[c2/2];
if(L1 > R2)
hi = c1-1;
else if(L2 > R1)
lo = c1+1;
else
break;
}
return (max(L1,L2)+ min(R1,R2))/2.0;
}
而对于割(Cut),如果割在‘#’上等于割在2个元素之间,割在数字上等于把数字划到2个部分,总是有以下成立:
LMaxi = (Ci-1)/2 位置上的元素
RMini = Ci/2 位置上的元素
如果C1或C2已经到头了怎么办?
这种情况出现在:如果有个数组完全小于或大于中值。假定n<m, 可能有4种情况:
C1 = 0 —— 数组1整体都在右边了,所以都比中值大,中值在数组2中,简单的说就是数组1割后的左边是空了,所以我们可以假定LMax1 = INT_MIN
C1 =2n —— 数组1整体都在左边了,所以都比中值小,中值在数组2中 ,简单的说就是数组1割后的右边是空了,所以我们可以假定RMin1=INT_MAX,来保证LMax2<RMin1恒成立
C2 = 0 —— 数组2整体在右边了,所以都比中值大,中值在数组1中 ,简单的说就是数组2割后的左边是空了,所以我们可以假定LMax2 = INT_MIN
C2 = 2m —— 数组2整体在左边了,所以都比中值小,中值在数组1中, 简单的说就是数组2割后的右边是空了,为了让LMax1 < RMin2 恒成立,我们可以假定RMin2 = INT_MAX
作者:bian-bian-xiong
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/4-xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-shu/
来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
如何理解c2 = m + n - c1
参考yyl9510的回答
为了将nums1和nums2插入#后的2m+2n+2个数平分,拆分过程中其实将c1和c2所在位置的#或数值分成了左右各一份。也就是总数变成了2m+2n+4,其中左边共有c1+1+c2+1个数,右边有(2m-(c1-1)+2n-(c2-1))也就是(2m+2n+2-c1-c2)个数,两边相等即c1+c2+2=2m+2n+2-c1-c2得到c2 = m + n - c1;
参考文献:参考文献