LeetCode:4. Median of Two Sorted Arrays
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)).
You may assume nums1 and nums2 cannot be both empty.
Example 1:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0
Example 2:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5
求两个数组的中值(可能不是数组中的值)。
思路一
这是官方提供的一种思路。如果我们想象把A数组分为leftA和rightA两部分,把B数组分为leftB和rightB两部分。然后leftA和leftB组成leftPart,rightA和rightB组成rightPart。我们只要保证下面两种条件:
- leftPart长度和rightPart长度相等,或者只相差1(假设leftPart比rightPart多一个)
- leftPart的最大值小于rightPart的最小值
那我们就可以得到中值一定是如下两种情况:
- 如果两边长度相等,中值median = (max_of_left + min_of_right) / 2.0
- 如果两者长度相差1,那么中值median肯定是左边的最大值max_of_left
其实这个做法就是将两个数组中较小部分合并到一个共同数组leftPart中,将两个数组中较大部分合并到一个共同数组rightPart中
。然后再取中值就很方便了。
class Solution(object):
def findMedianSortedArrays(self, 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 = 0
imax = m
half_len = ((int)((m + n + 1) / 2))
print("imin : %d, imax : %d , half_len : %d"%(imin,imax,half_len))
while imin <= imax:
i = (int)((imin + imax) / 2)
j = half_len - i
print("i : %d, j : %d "%(i,j))
if i < m and B[j-1] > A[i]:
print("A[i] : %d, B[j-1] : %d "%(A[i],B[j-1]))
# i is too small, must increase it
imin = i + 1
elif i > 0 and A[i-1] > B[j]:
print("A[i-1] : %d, B[j] : %d "%(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])
print("max_of_left : ",max_of_left)
if (m + n) % 2 == 1:
print("(m + n) % 2 == 1 , max_of_left : ",max_of_left)
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])
print("min_of_right : ",min_of_right)
return (max_of_left + min_of_right) / 2.0
思路二:转化为求第K小的值
首先转成求A和B数组中第k小
(两数组长度和的一半)的数的问题, 然后用k/2在A和B中分别找。
比如k = 6(n=12 or n=13), 分别看A和B中的第3个数
, 已知 A1 < A2 < A3 < A4 < A5… 和 B1 < B2 < B3 < B4 < B5…。如果A3 <= B3, 那么第6小的数肯定不会是A1, A2, A3, 因为最多有两个数小于A1(可能是B1<B2<A1), 三个数小于A2(可能是A1<B1<B2<A2), 四个数小于A3(可能是A1<A2<B1<B2<A3)
。 关键点是从 k/2 开始来找。
B3至少大于5个数
, 所以第6小的数有可能是B1 (A1 < A2 < A3 < A4 < A5 < B1), 有可能是B2 (A1 < A2 < A3 < B1 < A4 < B2), 有可能是B3 (A1 < A2 < A3 < B1 < B2 < B3)。而B3后面的因为至少大于6个数,所以肯定不会是第6小的数了。那就可以排除掉A1, A2, A3, 转成求A4, A5, … B1, B2, B3, …这些数中第3小的数的问题, k就被减半了
。每次都假设A的元素个数少, pa = min(k/2, lenA)的结果可能导致k == 1或A空, 这两种情况都是终止条件。
发问,为什么要从k/2开始寻找,依旧k = 6, 我可以比较A1 和 B5的关系么,可以这样做,但是明显的问题出现在如果A1 > B5,那么这个第6小的数应该存在于B6和A1中。
如果A1 < B5,这个时间可能性就很多了,比如A1 < A2 < A3 < A4 < B1 < B2,各种可能,无法排除元素,所以还是要从k/2开始寻找。
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
# k:A,B两数组中第k大的数
def findKth(A, B, k):
if len(A) == 0:
return B[k-1]
if len(B) == 0:
return A[k-1]
if k == 1:
return min(A[0], B[0])
a = A[k // 2 - 1] if len(A) >= k // 2 else None
b = B[k // 2 - 1] if len(B) >= k // 2 else None
if b is None or (a is not None and a < b):
return findKth(A[k // 2:], B, k - k // 2)
else:
return findKth(A, B[k // 2:], k - k // 2) # 这里要注意:因为 k/2 不一定 等于 (k - k/2)
n = len(nums1) + len(nums2)
if n % 2 == 1:
return findKth(nums1, nums2, n // 2 + 1)
else:
# 偶数的情况要找到中间两个数,然后求均值。
smaller = findKth(nums1, nums2, n // 2)
bigger = findKth(nums1, nums2, n // 2 + 1)
return (smaller + bigger) / 2.0
THE END.