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)).
Example 1:
nums1 = [1, 3]nums2 = [2]The median is 2.0Example 2:
nums1 = [1, 2]nums2 = [3, 4]The median is (2 + 3)/2 = 2.5题目大意:求得两个升序数组的中位数(根据两个数组长度和中位数定义稍有区分)
题目限定:The overall run time complexity should be O(log (m+n)).
题目思路:看到这个时间复杂度要求,我最先想到的是二叉树相关的数据结构。
如果能做出一个平衡二叉树,左子树都是小于等于根节点,右子树都是大于根节点,那这题目岂不是很简单吗?简直无脑输出。
仔细想想是不行的,因为将所有数字加入树,需要O(m+n),而把元素准确的放入合适位置,需要O(log2(m+n)),这俩复杂度相乘,肯定超时了。
其实还是我没见过世面,看了题解,脑洞打开,如果采用的是二分法搜索,刚好满足复杂度O(log2(m+n))。
以下是引用的题目解析(膜拜):
M MissMary Reputation: 800To solve this PRoblem, we need to understand "What is the use of median". In statistics, the median is used for
dividing a set into two equal length subsets, that one subset is always greater than the other
. If we understand the use of median for dividing, we are very close to the answer.First let's cut A into two parts at a random position i:
left_A | right_AA[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]Since A has m elements, so there are m+1 kinds of cutting( i = 0 ~ m ). And we know: len(left_A) = i, len(right_A) = m - i . Note: when i = 0 , left_A is empty, and when i = m , right_A is empty.
With the same way, cut B into two parts at a random position j:
left_B | right_BB[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]Put left_A and left_B into one set, and put right_A and right_B into another set. Let's name them left_part and right_part :
left_part | right_partA[0], A[1], ..., A[i-1] | A[i], A[i+1], ..., A[m-1]B[0], B[1], ..., B[j-1] | B[j], B[j+1], ..., B[n-1]If we can ensure:
1) len(left_part) == len(right_part)2) max(left_part) <= min(right_part)then we divide all elements in {A, B} into two parts with equal length, and one part is always greater than the other. Then median = (max(left_part) + min(right_part))/2.
To ensure these two conditions, we just need to ensure:
(1) i + j == m - i + n - j (or: m - i + n - j + 1) if n >= m, we just need to set: i = 0 ~ m, j = (m + n + 1)/2 - i(2) B[j-1] <= A[i] and A[i-1] <= B[j]ps.1 For simplicity, I presume A[i-1],B[j-1],A[i],B[j] are always valid even if i=0/i=m/j=0/j=n . I will talk about how to deal with these edge values at last.
ps.2 Why n >= m? Because I have to make sure j is non-nagative since 0 <= i <= m and j = (m + n + 1)/2 - i. If n < m , then j may be nagative, that will lead to wrong result.
So, all we need to do is:
Searching i in [0, m], to find an object `i` that: B[j-1] <= A[i] and A[i-1] <= B[j], ( where j = (m + n + 1)/2 - i )And we can do a binary search following steps described below:
<1> Set imin = 0, imax = m, then start searching in [imin, imax]<2> Set i = (imin + imax)/2, j = (m + n + 1)/2 - i<3> Now we have len(left_part)==len(right_part). And there are only 3 situations that we may encounter: <a> B[j-1] <= A[i] and A[i-1] <= B[j] Means we have found the object `i`, so stop searching. <b> B[j-1] > A[i] Means A[i] is too small. We must `ajust` i to get `B[j-1] <= A[i]`. Can we `increase` i? Yes. Because when i is increased, j will be decreased. So B[j-1] is decreased and A[i] is increased, and `B[j-1] <= A[i]` may be satisfied. Can we `decrease` i? `No!` Because when i is decreased, j will be increased. So B[j-1] is increased and A[i] is decreased, and B[j-1] <= A[i] will be never satisfied. So we must `increase` i. That is, we must ajust the searching range to [i+1, imax]. So, set imin = i+1, and goto <2>. <c> A[i-1] > B[j] Means A[i-1] is too big. And we must `decrease` i to get `A[i-1]<=B[j]`. That is, we must ajust the searching range to [imin, i-1]. So, set imax = i-1, and goto <2>.When the object i is found, the median is:
max(A[i-1], B[j-1]) (when m + n is odd)or (max(A[i-1], B[j-1]) + min(A[i], B[j]))/2 (when m + n is even)Now let's consider the edges values i=0,i=m,j=0,j=n where A[i-1],B[j-1],A[i],B[j] may not exist. Actually this situation is easier than you think.
What we need to do is ensuring that
Searching i in [0, m], to find an object `i` that: (j == 0 or i == m or B[j-1] <= A[i]) and (i == 0 or j == n or A[i-1] <= B[j]) where j = (m + n + 1)/2 - imax(left_part) <= min(right_part)
. So, if i and j are not edges values(means A[i-1],B[j-1],A[i],B[j] all exist), then we must check both B[j-1] <= A[i] and A[i-1] <= B[j]. But if some of A[i-1],B[j-1],A[i],B[j] don't exist, then we don't need to check one(or both) of these two conditions. For example, if i=0, then A[i-1] doesn't exist, then we don't need to check A[i-1] <= B[j]. So, what we need to do is:And in a searching loop, we will encounter only three situations:
<a> (j == 0 or i == m or B[j-1] <= A[i]) and (i == 0 or j = n or A[i-1] <= B[j]) Means i is perfect, we can stop searching.<b> j > 0 and i < m and B[j - 1] > A[i] Means i is too small, we must increase it.<c> i > 0 and j < n and A[i - 1] > B[j] Means i is too big, we must decrease it.Thank @Quentin.chen , him pointed out that:
m <= n, i < m ==> j = (m+n+1)/2 - i > (m+n+1)/2 - m >= (2*m+1)/2 - m >= 0 m <= n, i > 0 ==> j = (m+n+1)/2 - i < (m+n+1)/2 <= (2*n+1)/2 <= ni < m ==> j > 0
andi > 0 ==> j < n
. Because:So in situation <b> and <c>, we don't need to check whether
j > 0
and whetherj < n
.Below is the accepted code:
def median(A, B): m, n = len(A), len(B) if m > n: A, B, m, n = B, A, n, m if n == 0: raise ValueError imin, imax, half_len = 0, m, (m + n + 1) / 2 while imin <= imax: i = (imin + imax) / 2 j = half_len - i if i < m and B[j-1] > A[i]: # i is too small, must increase it imin = i + 1 elif i > 0 and A[i-1] > B[j]: # i is too big, must decrease it imax = i - 1 else: # i is perfect if i == 0: max_of_left = B[j-1] elif j == 0: max_of_left = A[i-1] else: max_of_left = max(A[i-1], B[j-1]) if (m + n) % 2 == 1: return max_of_left if i == m: min_of_right = B[j] elif j == n: min_of_right = A[i] else: min_of_right = min(A[i], B[j]) return (max_of_left + min_of_right) / 2.02 years ago reply quote附上AC的编码:
public class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { double median=0; int[] arraylonger=(nums1.length>nums2.length)?nums1:nums2; int[] arrayshorter=(nums1.length<=nums2.length)?nums1:nums2; //if(arraylonger==arrayshorter)System.out.println("went wrong!~"); int imin=0,imax=arrayshorter.length; int i=0,j=0,maxleft=0,minright=0,ll=arraylonger.length,sl=arrayshorter.length; while(imin<=imax){ i=(imin+imax)/2; //指示在较短数组中的index j=(ll+sl)/2-i; /** * 注意,在这里长数组下标的计算公式不同,自己给自己解释下 * 因为i+j=m-i+n-j,直接得到的应当是j=(m+n)/2-i。因为整数的取整规则,如此计算得到的j会比j=(m+n+1)/2-i小1,也会比实际浮点数运算的结果要小一点。 * 这可以理解为,当俩数组的长度总和为奇数时,右边的数字要比左边的多一个,那么此时的中位数就是右边里最小的那一个了(即代码25到31行与原大神解释的出入)。 */ if(i>0 && arrayshorter[i-1]>arraylonger[j]){ imax=i-1; }else if(i<sl && arraylonger[j-1]>arrayshorter[i]){ imin=i+1; }else{ if(i==sl)minright=arraylonger[j]; else if(j==ll)minright=arrayshorter[i]; else minright=Math.min(arrayshorter[i], arraylonger[j]); if((sl+ll)%2==1){ median = minright; break; } if(i==0)maxleft=arraylonger[j-1]; else if(j==0)maxleft=arrayshorter[i-1]; else maxleft=Math.max(arraylonger[j-1], arrayshorter[i-1]); return (maxleft+minright)/2.0; } } return median; }}AC的结果(果然上对了车,南辕北辙也不怕了):
新闻热点
疑难解答