题目:
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.0
Example 2:
nums1 = [1, 2] nums2 = [3, 4]The median is (2 + 3)/2 = 2.5
大致就是有两个排好序的数列,找到数列合并后的中位数,对时间有要求不可暴力。由于刚开始刷leetcode没啥思路,参考了评论里第一个的log(min(m,n))的,但他的源代码跑出来差不多只击败了20%的样子,重新写了下。
思路:
中位数的意思是在这个数列中比中位数小的数和比中位数大的数,数量相等。对于两个数列而言,问题相当于对两个数列分别划一刀(找出如下的a,b),每刀左面的部分【第一个数列左边的个数,加上第二个数列左边的个数】之和的数量等于右边的数量(或差一个)
left_part | right_part
nums1[0],nums1[1],...,nums1[a-1]|nums1[a],nums1[a+1],...,nums1[n-1]
nums2[0],nums2[1],...,nums2[b-1]|nums2[b],nums2[b+1],...,nums2[m-1]
【最后要达到的是刀左面的子序列中最大的元素不能超过右边子序列中最小的元素,当然两个数列是按照大小排序好的的,只要保证边界处的值符合条件即可】
然后输出右边两个子序列中最小的元素(对应两个数列总个数之和为奇数),或者找出右边两个子序列中最小的元素和左边两个子序列中最大的元素的平均(对应两个数列总个数之和为偶数)。
实现过程:
1、首先要初始化这俩数列的位置,比如我们搜索的是长度较小的数列,可以先比较长度,把短的记作m.
2、初始化最早刀的位置,不妨把刀放在两个数列的中间。
3、比较刀两侧值的大小,先调整位于较短数列上的刀的位置(用二分法),然后立刻根据a+b=(m+n)/2调整位于较长数列上的刀的位置。
4、循环3,知道满足条件
注意事项:
1、在找两刀位置的时候始终要注意的是两刀左边的总个数要不变,a+b=(m+n)/2;
2、如果有一个数列是空的时候直接输出另一个数列的中位数就可以了;
3、为了程序简单,我把很多情况先拿出去了,比如当刀在任何一个数列最左侧或者最右侧的时候,这时候只需要一次比较基本就可以算出结果了,如下例:
nums1=[1,3,5,7,9]
nums2=[10,11]
这时候到应该在nums1=[1,3,5,7||||||14], nums2=[||||||10,11]这种情况的判别方法是比较nums2的最小的数是否大于“新数列”的中间位置的数,新数列的意思是nums2可以插到nums1刀右侧以后的情况(如举例所示),当然很多情况是没有这个新数列的因为nums2的第一个元素可能要小于nums1刀左面最大的元素。【如果认定刀在nums2最左端,nums1刀的位置根据a+b=(n+m)/2是可以计算的】
4、如果不满足3的条件,那么在二分找nums2刀位置的时候无论刀在哪里,刀左右两端总有至少一个的元素,可以设置一个left记录可能的最左端的位置,right记录最右端的位置。
5、leetcode里5/2=2,要float(5)/2才等于2.5这个需要注意。
附源代码:
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
n = len(nums1)
m = len(nums2)
half_len=int((n + m) / 2)
if n < m:
n, m, nums1, nums2 = m, n, nums2, nums1
if m == 0:
if n%2==1: return nums1[int(n/2)]
else: return float(nums1[int(n/2)]+nums1[int(n/2)-1])/2
else:
if n == 1: return float((nums1[0] + nums2[0])) / 2
if (n + m) % 2 == 1: # single
if nums2[m - 1] <= nums1[half_len - m]:#half_len - m + 1 - 1
return nums1[half_len - m]
elif nums2[0] >= nums1[half_len-1]:
return min(nums2[0],nums1[half_len])
else:
a = int(n / 2)
b = half_len - a
left = 1
right = m - 1
while nums1[a] < nums2[b - 1] or nums1[a - 1] > nums2[b]:
#while b!=right and b!=left:
if nums1[a] < nums2[b - 1]:
right = b - 1
b = int((b + left) / 2)
a = half_len - b
else:
left = b - 1
b = int((b + right) / 2) + 1
a = half_len - b
result = min(nums1[a], nums2[b])
return result
else: # double
if nums2[m - 1] <= nums1[int((n + m) / 2) - m]: # left
if int((n + m) / 2) - m > 0:
return float((nums1[int((n + m) / 2) - m] + max(nums1[int((n + m) / 2) - m - 1], nums2[m - 1]))) / 2
else:
return float((nums1[int((n + m) / 2) - m] + nums2[m - 1])) / 2
elif nums2[0] >= nums1[int((n + m) / 2) - 1]: # right
if int((n + m) / 2) < n:
return float((nums1[int((n + m) / 2) - 1] + min(nums1[int((n + m) / 2)], nums2[0]))) / 2
else:
return float((nums1[int((n + m) / 2) - 1] + nums2[0])) / 2
else: # double
a = int(n / 2)
b = half_len - a
left = 1
right = m - 1
while nums1[a] < nums2[b - 1] or nums1[a - 1] > nums2[b]:
if nums1[a] < nums2[b - 1]:
right = b - 1
b = int((b + left) / 2)
a = half_len - b
else:
left = b - 1
b = int((left + right) / 2) + 1
a = half_len - b
result = float((max(nums1[a - 1], nums2[b - 1]) + min(nums1[a], nums2[b]))) / 2
return result
效果:
关于代码:
第一次刷leetcode,也不是计算机专业,难免有不专业的地方,我调整了一下部分代码,但是提交后速度还没有这个快,老司机勿喷...
我觉得可能导致速度比较快的原因是我代码中判断太多了,使得对于测试中很多特殊例具有很快的处理速度,但如果用于较大规模的计算时就不一定好了,一定存在很多问题,大家可以快给我指出来~~~