力扣刷题(python)50天——第二天:寻找两个有序数组的中位数
题目描述
给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法
由于设置了时间复杂度为(log(m + n)),故采用二分法进行操作。
对于中位数,我们通常是通过排序算法来做(如sort),但排序算法一般需要遍历整个列表,时间复杂度至少为m+n,因此需要使用二分法。对于找中位数,我们有两种思路:
1
中位数将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。如果我们去掉其中一个数组比中位数小的k个数,再去掉另一个数组中比中位数大的k个数,得到的合并子数组的中位数和原来的中位数相同。
eg:[1,2,3],[1,2,3] => [1,1,2,2,3,3]
去除元素[2,3],[1,2] => [1,2,2,3]中位数没变
所以假设两个数组的中位数分别是a[m1],b[m2]
if a[m1] == b[m2] ,那么刚好有一半的元素小于a[m1],那么a[m1]就是要找的中位数。参考上面的列子
if a[m1] < b[m2],根据定理1可知,这个中位数只可能出现在a[n1/2 ~ n1-1]或者b[0 ~ n2/2].也就是说合并这两个数组的中位数和原来的数组合并的数组的中位数是一样的。 根据定理2可知:
1.数组长度一样的时候,去除掉一半是合理的。
2.数组长度不一样,这么做中位数可能发生变化。解决方案就是去除掉相同个数的元素。假设n1 < n2, 两个数组就去掉n1/2个元素。那就不在是上面的范围(a[n1/2 ~ n1-1]或者b[0 ~ n2/2]),而是a[n1/2 ~ n1-1]或者b[0 ~ (-n1/2+n2-1)].
结论就是:只能删除a的n1/2(向下取整)
if a[m1] > b[m2],和上面分析类似,中位数只能出现在a的前半段或者b的后半段。也就是说a[0 ~ n1/2]和b[n1/2 ~ n2-1]的中位数和原来的中位数相同。
此方法中对于二分法的使用不明确
参考:
作者:GhostintheCode
链接:https://www.jianshu.com/p/6eab8e87a9de
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
参考链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/4-xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-shu/
现对其进行解释补充:
首先我们推导出了中位数的充要条件:lmax1<rmin2 and lmax2<rmin1,并且左右长度相等,或左边长度为合列表总长的一半。
后,我们遇到的最大问题是,列表合并后,m+n总数可能为奇, 也可能为偶,所以我们得想法让m+n总是为偶数,才能适用于我们的充要条件。
通过虚拟加入‘#’,我们让m转换成2m+1 ,n转换成2n+1, 两数之和就变成了2m+2n+2,恒为偶数。
注意是虚拟加,其实根本没这一步。这部仅用于理解系数的关系,其最大的作用是
而对于割(Cut),如果割在‘#’上等于割在2个元素之间,割在数字上等于把数字划到2个部分,总是有以下成立:
LMaxi = (Ci-1)/2 位置上的元素
RMini = Ci/2 位置上的元素
因此我们成功跨过对奇偶数的讨论,转而将两者计数方式统一
解答
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
l1,l2=len(nums1),len(nums2)
if l1>l2:
nums1,nums2,l1,l2=nums2,nums1,l2,l1
if l1==0:
if l2%2==1:
return nums2[l2/2]
else:
return (nums2[l2/2]+nums2[l2/2-1])/2.0
left_point=0
right_point=2*l1+1
while True:
i=(left_point+right_point)//2
j=l1+l2-i
if i!=0 and i!=2*l1:
lmax1=nums1[(i-1)//2]
rmin1=nums1[i//2]
elif i==0:
lmax1=float('-inf')
rmin1=nums1[i//2]
elif i==2*l1:
lmax1=nums1[(i-1)//2]
rmin1=float('inf')
if j!=0 and j!=2*l2:
lmax2=nums2[(j-1)//2]
rmin2=nums2[j//2]
elif j==0:
lmax2=float('-inf')
rmin2=nums2[j//2]
elif j==2*l2:
lmax2=nums2[(j-1)//2]
rmin2=float('inf')
if lmax1>rmin2:
left_point,right_point=left_point,i
elif lmax2>rmin1:
left_point,right_point=i,right_point
else:
return (max(lmax1,lmax2)+min(rmin1,rmin2))/2.0
心得
1.通过本题对时间复杂度以及二分法理解加深了
2.本题的核心在于对中位数本质的理解与运用,并且设置了#来归化奇偶,#帮助理解代码中的系数。
执行结果
取最好一次
执行用时 :88 ms, 在所有 Python 提交中击败了98.04%的用户
内存消耗 :11.9 MB, 在所有 Python 提交中击败了29.13%的用户
参考资料:
时间复杂度:
https://www.jianshu.com/p/f4cca5ce055a
https://blog.csdn.net/qq_41523096/article/details/82142747