文章目录
- 1题目理解
- 2 二分查找解题
- 2.1中位数的定义
- 2.2 数组切分
- 2.3分析条件
1题目理解
输入:2个已经排序号的int数组nums1,nums2
输出:这两个数组合并后的中位数
要求:m是nums1的长度,n是nums2的长度。时间复杂度应该是O(log(m+n))。
例子:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
2 二分查找解题
这道题目如果不要求时间复杂度的话,很简单。把两个数组合并。如果长度为奇数,找到中间位置的下标返回该元素。如果长度是偶数,则找到中间两个元素,求平均值返回。要符合时间复杂度要求就要用二分。以下内容来源于力扣,只看解法四。
这种解法在一刷的时候没看明白,现在二刷更清楚了。知道怎么从题目描述,到最终的条件。知道为什么这样做是对的。
2.1中位数的定义
中位数将一个数组分成两部分,左边元素个数等于右边元素个数,并且左边元素的值都小于右边元素的值。
2.2 数组切分
在数组中,我们在任意位置i,将数组切分为2部分。
由于数组A中有m个元素,所以数组A有m+1种切分方法。
i∈[0,m]i\in[0,m]i∈[0,m],那么
len(leftA)=i,len(rightA)=m−ilen(left_A)=i,len(right_A)=m-i len(leftA)=i,len(rightA)=m−i
采用同样的方式,在位置j,将数组B切分为两部分。
j∈[0,n]j\in[0,n]j∈[0,n],那么
len(leftB)=j,len(rightB)=n−jlen(left_B)=j,len(right_B)=n-j len(leftB)=j,len(rightB)=n−j
将left_A和left_B一起合并形成left_part,将right_A和right_B一起合并形成right_part。
当A和B的长度是偶数的时候,如果len(left_part)=len(right_part),并且max(left_part)<=min(right_part),
那么中位数=max(left_part)+min(right_part)2中位数=\dfrac{max(left\_part)+min(right\_part)}{2}中位数=2max(left_part)+min(right_part)
当A和B的长度是奇数的时候,如果len(left_part)=len(right_part)+1,并且max(left_part)<=min(right_part),那么
中位数=max(left_part)中位数=max(left\_part)中位数=max(left_part)
2.3分析条件
对于奇偶情况不同,长度条件不一样,我们可以做合并。要想得到中位数需要符合上面两个条件。这两个条件可以这样理解。
1 i+j=m-i+n-j (m+n为偶数)或者 i+j=m-i+n-j+1(m+n为奇数)。等号左侧是前一部分元素个数,等号右侧是后一部分元素个数。将i,j都转到等号左边,做变换,得到i+j=m+n+12i+j=\dfrac{m+n+1}{2}i+j=2m+n+1。
具体过程是,m+n为偶数,对于i+j=m-i+n-j ,得到2*(i+j)=m+n,进一步i+j=m+n2i+j=\dfrac{m+n}{2}i+j=2m+n,因为m+n为偶数,其实m+n2=m+n+12\dfrac{m+n}{2}=\dfrac{m+n+1}{2}2m+n=2m+n+1。因为这里取的是整除,所以+1不会影响结果。例如42=2{\dfrac{4}{2}=2}24=2,4+1=5,52=2{\dfrac{5}{2}=2}25=2。所以此时,i+j=m+n+12i+j=\dfrac{m+n+1}{2}i+j=2m+n+1
m+n为奇数, i+j=m-i+n-j+1,得到2*(i+j)=m+n+1,进一步得到i+j=m+n+12i+j=\dfrac{m+n+1}{2}i+j=2m+n+1。
所以条件1就是i+j=m+n+12i+j=\dfrac{m+n+1}{2}i+j=2m+n+1
2 0<=i<=m0<=i<=m0<=i<=m,0<=j<=n0<=j<=n0<=j<=n。如果我们规定m<=n,那么对于任意的i∈[0,m]i \in[0,m]i∈[0,m],都有j=m+n+12−i∈[0,n]j=\dfrac{m+n+1}{2}-i \in[0,n]j=2m+n+1−i∈[0,n]。这是因为不能让j是负数。可以举一个反例。例如m=10,n=4,当i=9的时候,j=7-9就是负数了。
3 max(left_part)<=min(right_part),的等价条件是A[i-1]<=B[j]并且B[j-1]<=A[i]。我们可以对该条件做进一步分析。
首先因为数组切分可能切在数组开始,也可能切在数组末尾。也就是说i=0,也有i=m,j=0,j=n的情况。这个时候我们假设A[-1]=B[-1]=−∞-\infty−∞,A[m+1]=B[n+1]=∞\infty∞。这样可以保证结果值不受影响。如果i=0,那么leftA=−∞left_A={-\infty}leftA=−∞,最大值肯定来源于leftBleft_BleftB。所以结果不影响。其他同理。
接着,我们需要证明A[i-1]<=B[j]并且B[j-1]<=A[i] 等价于,找到最大的i使得A[i-1]<=B[j],j=m+n+12−ij=\dfrac{m+n+1}{2}-ij=2m+n+1−i。
这是因为当i从0到m主键增加,j会从n到0逐渐减小,A[i-1]递增,B[j]逐渐递减。总会找到一个最大的i,使得A[i-1]<=B[j]。
如果i是最大的,那说明i+1不满足,说明A[i]>B[j-1],也就是说B[j-1]<A[i]。和原来的条件要求是一样的。
因此,我们可以在[0,m]上找到最大的i,使得A[i-1]<=B[j],那么i,j就是切分点。前一部分元素的最大值,以及后一部分的最小值,才可能是数组的中位数。
class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int m = nums1.length;int n = nums2.length;//第一个数组一定是短的,这是因为 我们要保证i和j都是大于0的数。因为l<=r的限制,那么如果i=m的时候,j就一定为负数了。if(m>n){return findMedianSortedArrays(nums2,nums1);}int l = 0, r =m;//A中元素有m个,有m+1种划分方法。这里的i表示leftA有几个元素。int median1=0,median2=0;while(l<=r){int i = (l+r)>>1;int j = (m+n+1)/2-i;int leftA = (i>0?nums1[i-1]:Integer.MIN_VALUE);int rightA = (i<m?nums1[i]:Integer.MAX_VALUE);int leftB = (j>0?nums2[j-1]:Integer.MIN_VALUE);int rightB = (j<n?nums2[j]:Integer.MAX_VALUE);if(leftA<=rightB){median1 = Math.max(leftA,leftB);median2 = Math.min(rightA,rightB);l = i+1;}else{r = i-1;}}return (m+n)%2==0?(median1+median2)/2.0:median1;}
}